经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Nginx » 查看文章
nginx做白名单和限流
来源:cnblogs  作者:SunArmy  时间:2024/2/19 9:20:40  对本文有异议

? 在我们生产环境中使用到了地图服务,每个月有免费请求次数,近一个月请求次数突然暴涨,导致直接开启付费模式,一个月上百刀的花销着实难扛,根据实际我们的业务使用情况,远达不到付费标准,故考虑做白名单和限流措施,基于以上情况并遇到春节急需快速处理,所以选择了最简单方便的方式,通过nginx做限流

? 我们都知道nginx里面是可以用lua脚本做一些稍微复杂些的逻辑处理的,要使用lua脚本需要编译lua解释器,时间有限我直接用了openresty,它集成了lua和nginx

1、openresty是什么?

OpenResty是一个基于Nginx的高性能Web平台,用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。具备下列特点:

具备Nginx的完整功能
基于Lua语言进行扩展,集成了大量精良的 Lua 库、第三方模块,允许使用Lua自定义业务逻辑、自定义库

二、OpenResty的安装

1、添加OpenResty仓库

  1. # 由于公共库中找不到openresty,所以需要添加openresty的源仓库
  2. yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
  3. # 注意,如果上面命令提示不存在,那就先安装一下
  4. yum install -y yum-utils

2. 安装OpenResty

  1. # 安装openresty
  2. yum install -y openresty
  3. # 安装OpenResty管理工具,帮助我们安装第三方的Lua模块
  4. yum install -y openresty-opm

3、目录结构

? 默认安装在/usr/local/openresty

看到里面有一个nginx目录,进去可以看到跟我们平常用的nginx是一模一样的,OpenResty就是在Nginx基础上集成了一些Lua模块

到这里我们就安装好了

7. 启动和运行

OpenResty底层是基于Nginx的,查看OpenResty目录的nginx目录,结构与windows中安装的nginx基本一致:

所以这个里面的nginx和平常的nginx是一样的

1)nginx配置文件
  1. worker_processes 1;
  2. events {
  3. worker_connections 1024;
  4. }
  5. http {
  6. server{
  7. listen 999;
  8. server_name localhost;
  9. location /mapbox/ {
  10. access_by_lua_file "/usr/local/openresty/nginx/lua_script/rule.lua";
  11. proxy_pass https://api.mapbox.com/;
  12. proxy_ssl_server_name on;
  13. }
  14. }
  15. }
2)lua脚本文件(白名单加限流)

通过两个redis的key,map_request_limitation:存放令牌数量,map_request_white_list:白名单列表;白名单的IP,无需限流,只有白名单之外的才需要限流

  1. -- 其实这两个值可以从redis 甚至可以给每个qrcode设置单独的速率和容积
  2. -- 但如果想监听桶的状态 需要持续的请求, 只有每次请求后才重新计算并更新桶状态 否则桶状态不变
  3. local tokens_per_second = 0.2 -- 生成速率 /s
  4. local max_tokens = 10 -- 最大溶剂
  5. local current_time = ngx.now()
  6. local path = ngx.var.uri
  7. local redis_key = "map_request_limitation"
  8. local redis_key_white_list = "map_request_white_list"
  9. local client_ip = ngx.var.remote_addr
  10. -- local redis_key = "path:" .. path
  11. -- 连接Redis
  12. local redis = require "resty.redis"
  13. local red = redis:new()
  14. red:set_timeout(1000)
  15. local ok, err = red:connect("127.0.0.1", 6379)
  16. if not ok then
  17. ngx.log(ngx.ERR, "Redis连接失败: ", err)
  18. return ngx.exit(500)
  19. end
  20. -- 权限校验
  21. local res, err = red:auth("123456")
  22. if not res then
  23. ngx.say("failed to authenticate: ", err)
  24. return
  25. end
  26. -- 发送 Lua 脚本(保证redis原子性操作)
  27. local script = [[
  28. local redis_key = KEYS[1]
  29. local redis_white_list_key = KEYS[2]
  30. local tokens_per_second = tonumber(ARGV[1])
  31. local max_tokens = tonumber(ARGV[2])
  32. local current_time = tonumber(ARGV[3])
  33. local client_ip = ARGV[4]
  34. -- ip是否存在列表中
  35. local is_in_whitelist, err = redis.call('sismember', redis_white_list_key, client_ip)
  36. if is_in_whitelist == 1 then
  37. return 1
  38. end
  39. -- 获取上次访问时间和令牌数量
  40. local res = redis.call('HMGET', redis_key, 'last_access_time', 'tokens')
  41. local last_access_time
  42. local last_tokens
  43. if res[1] and res[2] then
  44. last_tokens = res[2]
  45. last_access_time = res[1]
  46. end
  47. -- 计算时间间隔
  48. local time_passed = current_time - (tonumber(last_access_time) or 0)
  49. -- 计算新的令牌数量
  50. last_tokens = last_tokens and tonumber(last_tokens) or max_tokens
  51. local new_tokens = math.min(max_tokens, last_tokens + time_passed * tokens_per_second)
  52. -- 判断令牌数量是否足够
  53. if new_tokens >= 1 then
  54. -- 消耗令牌
  55. redis.call('HMSET', redis_key, 'last_access_time', current_time, 'tokens', new_tokens - 1)
  56. return 1
  57. else
  58. return 0
  59. end
  60. ]]
  61. -- 执行脚本
  62. local result = red:eval(script, 2, redis_key, redis_key_white_list,tokens_per_second, max_tokens,current_time,client_ip)
  63. if result == 1 then
  64. -- 成功
  65. ngx.log(ngx.INFO, "成功")
  66. else
  67. -- 令牌不足
  68. ngx.status = ngx.HTTP_TOO_MANY_REQUESTS
  69. ngx.say("OVERLOAD!!!!",result)
  70. return ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS)
  71. end
  72. -- 返还redis连接到连接池
  73. local ok, err = red:set_keepalive(10000, 100)
  74. if not ok then
  75. ngx.log(ngx.ERR, err)
  76. end

启动之后当通过这个999端口访问之后,我们在redis里面可以看到以下两个key,白名单可以自行添加,即时生效

原文链接:https://www.cnblogs.com/KingArmy/p/18019489

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号