前言
瞭解到lua腳本和nginx的搭配之後,一直想知道這東西能用來做點什麼。於是動手實踐了一下... 下面開始正文
前期準備
獲取openresty:openresty官網(其實就是一個通過lua擴展的nginx服務器) http://openresty.org/cn/getting-started.html
獲取redis:redis中文網站 http://www.redis.cn/
功能概述
1 灰度測試
用戶訪問某個地址,根據他的特徵值來給他返回不同的頁面,實現灰度發佈功能。在這個demo中我們使用用戶ip
2 防刷限流
在這個例子中,如果用戶在5秒內超過三次訪問,就禁止該用戶繼續訪問。通過redis的expire實現
爲什麼要用openresty做
畫一張圖幫助大家理解。這樣做的好處在於,將流量擋在業務之外,利用nginx、lua、redis高性能的特性來緩解業務服務器的壓力。
代碼實現與解析
準備兩個接口
在這裏我用java和golang分別做了一個hello world,運行在8080、8081端口上。java作爲默認版本,golang作爲測試版本。如下
@RestController
@RequestMapping("/")
public class HelloWorldController {
@RequestMapping("/hello")
public String hello(){
return "hello world by java !";
}
}
func main() {
http.HandleFunc("/hello",helloHandler)
err := http.ListenAndServe(":8081",nil)
if err != nil {
fmt.Printf("listen failed..., err:%v\n ",err)
return
}
}
func helloHandler(writer http.ResponseWriter,req *http.Request){
_,_ = fmt.Fprintln(writer,"hello world by GoLang!")
}
然後寫一個nginx(openresty)的conf文件內容如下
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
upstream client1 {
server 192.168.27.1:8080;
}
upstream client2 {
server 192.168.27.1:8081;
}
server {
listen 80;
location ^~ /hello {
content_by_lua_file /usr/local/openresty/workspace/conf/hello_abtest.lua;
}
location @client1 {
proxy_pass http://client1;
}
location @client2 {
proxy_pass http://client2;
}
}
}
準備好上面的hello_abtest.lua(當然我在這裏面還寫了限流的邏輯)
local ok, err = cache.connect(cache, '127.0.0.1', 6379)
if not ok then
ngx.say("failed to connect:",err)
return
end
local local_ip = ngx.req.get_headers()["X-Real-IP"]
if local_ip == nil then
local_ip = ngx.req.get_headers()["x_forwarded_for"]
end
if local_ip == nil then
local_ip = ngx.var.remote_addr
end
if cache:setnx(local_ip.."lock",0) == 0 then
local times = cache:incr(local_ip.."lock")
if times >= 3 then
ngx.say("429 too many requests")
return
end
else
cache:setnx(local_ip.."lock",0)
cache:expire(local_ip.."lock",5)
end
local intercept = cache:get(local_ip)
if intercept==local_ip then
ngx.exec("@client2")
return
end
ngx.exec("@client1")
local ok, err = cache:close()
if not ok then
ngx.say("failed to close: ",err)
return
end
簡單解釋一下上面的邏輯。
限流:拿到客戶機的ip。然後向redis中寫一個key叫loca_ip.."lock"(對應xxx.xxx.xxx.xxxlock),如果這個key已經存在的話就給它加1,如果不存在就創建它 初始化爲0,並設置過期時間爲5秒。
灰度測試:把用客戶機ip作爲key去查詢redis,如果得到的結果與local_ip不同,則代表該客戶不具備權限,給他返回正式資源;否則,則代表該用戶在測試名單中,爲他返回測試資源。
測試結果
首先清空redis:
然後測試:
將192.168.27.1加入白名單
刷新網頁:
多點幾次試試:
寫在最後的話
其實還有很多種玩法,比如nginx+lua+kafka還能實現很多有趣的功能。由於nginx、lua、redis都有高性能的特點,所以將一些簡單的業務寫在這一級別,可以阻止不必要的流量進入業務系統帶來不必要的壓力。
有機會再更新kafka的玩法,yes! 吐槽:找個java工作太難了!!!
參考資料:
OpenResty官網 http://openresty.org/cn/
Redis中文網站 http://www.redis.cn/
參考博客:
Openresty+lua+redis實現灰度發佈 https://www.cnblogs.com/Eivll0m/p/6774622.html
Nginx+lua+redis防刷和限流 https://blog.csdn.net/fenglvming/article/details/51996406