版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cn_yaojin/article/details/81737258
编译openresty:
./configure --prefix=/usr/local/openresty --with-luajit --with-http_ssl_module --with-stream --with-stream=dynamic --with-stream_ssl_module --with-stream_realip_module --with-mail --with-mail=dynamic --with-http_v2_module --with-http_realip_module --user=root --group=root
如果提示需要 pcre,那么pcre 的安装命令:
yum install -y pcre pcre-devel
如果缺失 perl, 点这儿
或者
yum install perl
因为使用到了,template.lua,因此需要下载:https://github.com/bungle/lua-resty-template/tree/master/lib/resty
1、nginx配置:
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
lua_shared_dict ip_blacklist 1m;
lua_shared_dict limit 10m;
gzip on;
#漏桶算法限流(限制请求速率为200 req/sec,并且允许100 req/sec的突发请求,就是说我们会把200以上300一下的请求请求给延迟,超过300的请求将会被拒绝)
lua_shared_dict my_limit_req_store 100m;
#server配置
include domains/*.conf;
}
domain文件夹下的 *.conf文件:
#限速模块
server {
listen 80;
server_name 192.168.58.128;
charset utf-8;
#模板生成html
location / {
default_type text/html;
content_by_lua_file ../lua/index.template.lua;
#root html; #站点目录
#index index.html index.htm;
}
location /fameapi {
default_type application/json;
access_by_lua_file ../lua/hight_limit_speed.lua;#限速模块
proxy_buffer_size 64k;
proxy_buffers 32 32k;
proxy_busy_buffers_size 128k;
proxy_pass http://fameapi;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass_header User-Agent;
client_max_body_size 100M;
proxy_connect_timeout 60;#nginx跟后端服务器连接超时时间(代理连接超时)
proxy_send_timeout 60;#后端服务器数据回传时间(代理发送超时)
proxy_read_timeout 60;#连接成功后,后端服务器响应时间(代理接收超时)
}
location /postgres {
internal;
default_type text/html;
set_by_lua_block $query_sql {return ngx.unescape_uri(ngx.var.arg_sql)}
postgres_pass pg_server;
rds_json on;
rds_json_buffer_size 16k;
postgres_query $query_sql;
postgres_connect_timeout 1s;
postgres_result_timeout 2s;
}
}
#文件
upstream fameapi {
server 192.168.0.124:8080 weight=1;
}
upstream pg_server {
postgres_server 192.168.0.103:5432 dbname=fame
user=postgres password=root;
postgres_keepalive max=800 mode=single overflow=reject;
}
2、LUA脚本
-- 获取限流的uri
-- 获取黑名单
-- 获取白名单
-- 获取最大访问次数
--连接到 redis
local redis = require "resty.redis_iresty"
local cache = redis:new()
-- 写入日志记录
function writeLog(str)
-- 系统当前时间
local time = os.time()
-- 格式化时间
local date = os.date("%Y%m%d",time)
file = io.open("d:/lua.limit."..date..".log","a+")
file:write(str)
file:write("\n")
file:close()
end
--写json
function writeJson(retTable)
local cjson = require "cjson"
--local retTable = {};retTable["success"] = false;
return cjson.encode(retTable);
end
--读取json
function readJson(str)
local cjson = require "cjson"
local c = cjson.decode(str)
return c
end
--response
function response2Client()
local retTable = {};
retTable["success"] = false;
retTable["msg"] ="网络繁忙,请稍后再试";
return writeJson(retTable)
end
--与response2Client方法 可以合并了
function responseMsg(msg)
local retTable = {};
retTable["success"] = false;
retTable["msg"] =msg;
return writeJson(retTable)
end
--获取请求uri
function getUri()
return ngx.var.uri
end
--获取客户端ip
function get_client_ip()
local headers=ngx.req.get_headers()
local ip_addr=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr or "0.0.0.0"
return ip_addr
end
--响应错误
function callerror()
ngx.header.content_type="application/json;charset=utf8"
ngx.say(response2Client())
ngx.exit(200);
end
--响应错误消息(与callerror可以合并了 )
function callErrorMsg(msg)
ngx.header.content_type="application/json;charset=utf8"
ngx.say(responseMsg(msg))
ngx.exit(200);
end
function respData2Client(obj)
ngx.header.content_type="application/json;charset=utf8"
local retTable = {};
retTable["success"] = true;
retTable["msg"] ="ok";
retTable["obj"] = obj;
ngx.say(writeJson(retTable))
ngx.exit(200);
end
--获取客户端ip
local ip_addr = get_client_ip()
local local_url = getUri()
--检测黑名单
function checkblacklist()
local is_black,err = cache:sismember('ip_blacklist',ip_addr)
if is_black==1 then
return callerror()
end
end
--检测白名单
function checkwhitelist()
local is_white,err = cache:sismember('ip_whitelist',ip_addr)
if is_white=='1' then
return callerror()
end
end
-- 从缓存中读取基本配置
function readBaseConfig()
local base = cache:get('FAME_GLOBAL_CONFIG_KEY')
if base then
local json = readJson(base)
return json
end
end
--全应用限流(所有接口的总限流入口)
function checklimit()
--get req limit
local json = readBaseConfig()
local max_req_limit = json.max_req_limit
local wait_req_limit = json.wait_req_limit
if not max_req_limit then
max_req_limit = 1
end
if not wait_req_limit then
wait_req_limit = 0
end
--每秒最大请求数
max_req_limit = tonumber(max_req_limit)
--超过最大请求数后,允许继续等待的请求数
wait_req_limit = tonumber(wait_req_limit)
--
if max_req_limit == 0 then
return callerror()
end
-- ngx.log(ngx.ERR,"应用限流速度: ", max_req_limit)
local limit_req = require "resty.limit.req"
-- 限制请求速率为200 req/sec,并且允许100 req/sec的突发请求
-- 就是说我们会把200以上300一下的请求请求给延迟
-- 超过300的请求将会被拒绝
local lim, err = limit_req.new("my_limit_req_store", max_req_limit, wait_req_limit)
if not lim then --申请limit_req对象失败
ngx.log(ngx.ERR,"failed to instantiate a resty.limit.req object: ", err)
return callerror()
end
-- 下面代码针对每一个单独的请求
local delay, err = lim:incoming('fame_api_limit', true)
if not delay then
if err == "rejected" then
return callerror()
end
ngx.log(ngx.ERR, "failed to limit req: ", err)
return callerror()
end
if delay > 0 then
-- 第二个参数(err)保存着超过请求速率的请求数
-- 例如err等于31,意味着当前速率是231 req/sec
local excess = err
-- 当前请求超过200 req/sec 但小于 300 req/sec
-- 因此我们sleep一下,保证速率是200 req/sec,请求延迟处理
ngx.sleep(delay) --非阻塞sleep(秒)
end
end
--限制同一个IP在某时间内url的请求次数(192.168.0.1 在10秒内访问url的次数),超过次数后终止该ip的请求
function checkUrlLimit()
local access_uri = ip_addr..local_url
local key = access_uri
local limit = ngx.shared.limit
local req
if limit then
req = limit:get(key)
end
--读取基本配置
local json = readBaseConfig()
--单位时间内最大请求数
local lock_limit_num = json.lock_limit_num
--单位时间
local lock_limit_seconds = json.lock_limit_seconds
if not lock_limit_num then
lock_limit_num = 100
end
if not lock_limit_seconds then
lock_limit_seconds = 10
end
--每秒最大请求数
lock_limit_num = tonumber(lock_limit_num)
--超过最大请求数后,允许继续等待的请求数
lock_limit_seconds = tonumber(lock_limit_seconds)
if req then
if req >=lock_limit_num then
--添加单黑名单
cache:sadd('ip_blacklist',ip_addr)
return callerror()
end
limit:incr(key,1)
else
--60S
limit:set(key,1,lock_limit_seconds)
end
ngx.log(ngx.ERR,"完成校验,即将进入content: ", "这是姚进的测试")
--content.test()
end
--从postgres 查询数据,并以JSON的格式展示
function queryDataPostgres()
local res = ngx.location.capture('/postgres',
{ args = {sql = "select user_id,user_name from fame_user where 1=1 limit 5 offset 0 " } }
)
local status = res.status
--读取查询到的数据,并将json格式转换成表格对象
local body = readJson(res.body)
if status == 200 then
--返回查询结果给客户端,以JSON的格式展示
return respData2Client(body)
else
return callerror()
end
end
-- api限速
function limitApiSpeed(key,max_req_limit,wait_req_limit,msg)
if not max_req_limit then
max_req_limit = 10
end
if not wait_req_limit then
wait_req_limit = 1
end
--每秒最大请求数
max_req_limit = tonumber(max_req_limit)
--超过最大请求数后,允许继续等待的请求数
wait_req_limit = tonumber(wait_req_limit)
--
if max_req_limit == 0 then
return callErrorMsg(msg)
end
local limit_req = require "resty.limit.req"
-- 限制请求速率为200 req/sec,并且允许100 req/sec的突发请求
-- 就是说我们会把200以上300一下的请求请求给延迟
-- 超过300的请求将会被拒绝
local lim, err = limit_req.new("my_limit_req_store", max_req_limit, wait_req_limit)
if not lim then --申请limit_req对象失败
ngx.log(ngx.ERR,"failed to instantiate a resty.limit.req object: ", err)
return callErrorMsg(msg)
end
-- 下面代码针对每一个单独的请求
-- 使用 请求地址 地址作为限流的key
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
return callErrorMsg(msg)
end
ngx.log(ngx.ERR, "failed to limit req: ", err)
return callErrorMsg(msg)
end
if delay > 0 then
-- 第二个参数(err)保存着超过请求速率的请求数
-- 例如err等于31,意味着当前速率是231 req/sec
local excess = err
-- 当前请求超过200 req/sec 但小于 300 req/sec
-- 因此我们sleep一下,保证速率是200 req/sec,请求延迟处理
ngx.sleep(delay) --非阻塞sleep(秒)
end
end
-- 查询限速的接口(限流的api,保存在redis的map中)
function getApiSpeedLimit(url)
local result = cache:hget('API_SPEED_LIMIT',url)
if result then
local json = readJson(result)
return json
end
end
--api限速
function checkApiUrl()
local ip = ngx.var.binary_remote_addr
local url = ngx.var.uri
local result = getApiSpeedLimit(url)
if result then
-- api接口是否启用
local flag =result.flag
-- api接口1秒内最大的请求数
local limit_api_count = result.limit_api_count
-- api接口1秒内达到最大请求数后,允许等待的线程数
local limit_api_realy = result.limit_api_realy
local msg msg = result.msg
if flag == 0 then
-- 正常的接口
limitApiSpeed(url,limit_api_count,limit_api_realy,msg)
else
--禁用的接口
callErrorMsg(msg)
end
end
end
--功能清单
function access_check()
queryDataPostgres()
checkblacklist()
checklimit()
checkUrlLimit()
end
--请求入口
access_check()
api接口限流,具体信息保存在redis的map中
package com.cn.common.apiurls;
import java.io.Serializable;
public class ApiSpeedLimitInfo implements Serializable {
/**
* 接口名称
*/
private String name;
private String url;
/**
* 消息
*/
private String msg;
/**
* 0-正常
* 1-禁用
*/
private int flag;
/**
* 接口限速- 1s内允许通过接口最大数目
*/
private int limit_api_count;
/**
* 接口限速后 1s内允许该接口等待线程的最大数目
*/
private int limit_api_realy;
public String getMsg() {
return msg;
}
public ApiSpeedLimitInfo setMsg(String msg) {
this.msg = msg;
return this;
}
public int getFlag() {
return flag;
}
public ApiSpeedLimitInfo setFlag(int flag) {
this.flag = flag;
return this;
}
public int getLimit_api_count() {
return limit_api_count;
}
public ApiSpeedLimitInfo setLimit_api_count(int limit_api_count) {
this.limit_api_count = limit_api_count;
return this;
}
public int getLimit_api_realy() {
return limit_api_realy;
}
public ApiSpeedLimitInfo setLimit_api_realy(int limit_api_realy) {
this.limit_api_realy = limit_api_realy;
return this;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
api示例:
html模板:
local template = require "resty.template"
-- 请求url
local url = ngx.var.uri
--查询参数
local code_no = ngx.var.arg_code_no
template.render("index.html",{ message =url,params=code_no})
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to OpenResty!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to OpenResty!</h1>
<p>Hi,{{message}}</p>
<p>收到的参数:{{params}}</p>
<p>For online documentation and support please refer to
<a href="https://openresty.org/">openresty.org</a>.<br/>
Commercial support is available at
<a href="https://openresty.com/">openresty.com</a>.</p>
<p><em>Thank you for flying OpenResty.</em></p>
</body>
</html>
3、域名配置,如下:
html5.api.conf配置如下:
#限速模块
server {
listen 80;
server_name 192.168.58.128;
charset utf-8;
location /fameapi {
default_type application/json;
access_by_lua_file ../lua/hight_limit_speed.lua;#限速模块
proxy_buffer_size 64k;
proxy_buffers 32 32k;
proxy_busy_buffers_size 128k;
proxy_pass http://fameapi;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass_header User-Agent;
client_max_body_size 100M;
proxy_connect_timeout 60;#nginx跟后端服务器连接超时时间(代理连接超时)
proxy_send_timeout 60;#后端服务器数据回传时间(代理发送超时)
proxy_read_timeout 60;#连接成功后,后端服务器响应时间(代理接收超时)
}
#查询postgres
location /postgres {
internal; #加上该参数后,禁止外部访问
default_type text/html;
set_by_lua_block $query_sql {return ngx.unescape_uri(ngx.var.arg_sql)}
postgres_pass pg_server;
rds_json on;
rds_json_buffer_size 16k;
postgres_query $query_sql;
postgres_connect_timeout 1s;
postgres_result_timeout 2s;
}
}
#文件
upstream fameapi {
server 192.168.0.124:8080 weight=1;
}
#postgres 查询
upstream pg_server {
postgres_server 192.168.0.103:5432 dbname=fame
user=postgres password=root;
postgres_keepalive max=800 mode=single overflow=reject;
}