nginx限流配置|8月更文挑戰

why?

本篇文章旨在幫助網站限制用戶訪問頻率,必定程度上擋住了部分爬蟲攻擊。
可經過擴展get_client_id 識別客戶端 或 HTTP_FORBIDDEN 來回傳給驗證碼。滑塊操做等人機驗證html

安裝OpenResty

OpenResty® 經過匯聚各類設計精良的 Nginx 模塊(主要由 OpenResty 團隊自主開發),從而將 Nginx 有效地變成一個強大的通用 Web 應用平臺。這樣,Web 開發人員和系統工程師能夠使用 Lua 腳本語言調動 Nginx 支持的各類 C 以及 Lua 模塊,快速構造出足以勝任 10K 乃至 1000K 以上單機併發鏈接的高性能 Web 應用系統。
OpenResty是一個基於Nginx的平臺,裏面提供了許多高質量的第三方模塊。咱們本次使用的是redis + lua組合,因此須要OpenResty來提供lua環境
點擊此處下載nginx

新建一個站點用於測試

package main
import "github.com/gofiber/fiber/v2"
func main() {
	app := fiber.New()

	app.Get("/", func(c *fiber.Ctx) error {
		return c.SendString("content content content")
	})

	app.Get("/api/member/get", func(c *fiber.Ctx) error {
		user := struct {
			UserName string `json:"userName"`
			Age      int    `json:"age"`
		}{UserName: "juejin", Age: 100}
		return c.JSON(user)
	})

	app.Listen(":8080")
}

複製代碼

在上述代碼中,咱們提供了一個http get /api/member/get的API,他返回了一堆json
在根節點則返回了一堆沒有意義的字符 訪問API截圖以下:git

image.png

將站點託管到nginx中

在server節點裏,指定特定的路徑名並使用lua script
此處403只是返回了一個靜態頁面,後面咱們能夠像開頭說的那樣換成驗證碼之類的...github

location ~ /api { #這裏能夠自定義路徑
    proxy_http_version 1.1;
    access_by_lua_file lua/access_limit.lua; #指定lua的路徑
    allow 127.0.0.1;
    proxy_pass http://127.0.0.1:8080;
    proxy_redirect default;
}

error_page 403 /403.html; #這個是咱們拒絕訪問時,返回給客戶端的響應
location = /403.html { 
    root   html;
    allow all;
}
複製代碼

lua 處理請求

Q:每次請求都要鏈接redis,會不會致使鏈接耗盡、資源緊張?
A:OpenResty爲咱們提供了鏈接池,資源是能夠重用的,不會致使資源緊張redis

咱們首先來肯定一下基本的參數:
block_client = 1 //用來記錄封鎖時長
limt_client = 5 //每秒訪問個數
local limit_expiry = 1 //記錄limit_expiry秒內的訪問
local max_count = 5 //limit_expiry秒內的訪問秒內的最大訪問次數json

主要方法解析:api

  1. get_client_id 方法:這個方法主要是用來識別用戶身份的。能夠取 ip 、 token 、 cookie 之類的作個簽名以當作用戶的訪問令牌的。這裏偷個懶,只是取了原ip
local function get_client_id()    
   local headers = ngx.req.get_headers()
   local clientId = headers["X-Real-IP"] or headers["x_forwarded_for"] or ngx.var["remote_addr"] or "0.0.0.0"
   return clientId
end
複製代碼
  1. 釋放鏈接
--從鏈接池關閉redis
local function close_redis(client)
   if not client then
       return
   end
   --將redis鏈接放回鏈接池
   local ok, err = client:set_keepalive(pool_max_idle_time, pool_size)
   if not ok then
       ngx.say("redis connct err:",err)
       return client:close()
   end
end
複製代碼

完整代碼:

gitee.com/cergou/juej…markdown

寫在最後

本次主要學習了lua來實現限流,性能上沒有帶來明顯的負荷
主要思路爲:設定一個N秒內過時的key,客戶端發起請求時經過get_client_id來識別用戶身份。每次請求時給這個key自增1,當此值超過咱們規定的最大值時。拒絕此用戶的請求,並將此用戶記錄到block_client。下次直接拒絕該用戶請求。block_client過時後。用戶又能正常訪問服務cookie

思惟導圖:併發

image.png

相關文章
相關標籤/搜索