- win机作为客户端, 使用 nginx , localhost:80
反向代理到 服务器端的 192.168.111.110:8081 nginx
- 服务器端的nginx 是一个集群, 配置一个拦截 /item/(\d+) 的请求
反向代理到win机的 tomcat 的 192.168.111.1:8081 和 192.168.111.1:8082
- 于是我们的业务逻辑就是
- 对于 /item/(\d+) 这种请求, 我们 先去 服务器端的nginx查 key , 这个要定义好
- 如果没有, 则去 redis (192.168.111.110:6380)
- 再没有 , 则去 使用http请求访问 tomcat , 控制器方法
需要指出的是 , 即便没有tomcat , 在缓存预备好后, nginx和redis 仍然可以 让用户成功访问网页 , 因为动态数据已经缓存了, 而静态数据本身就在nginx的html里
![eeed8bdd38bb4486a2fc868b8bb05ac7.png](https://img-blog.csdnimg.cn/direct/eeed8bdd38bb4486a2fc868b8bb05ac7.png)
- 在 win机的 nginx的配置 , 在页面里直接使用 localhost访问, 80为默认端口 , 不需要写
这里反向代理到 服务器的nginx , 192.168.111.110:8081
#user nobody;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
upstream nginx-cluster{
server 192.168.111.110:8081;
}
server {
listen 80;
server_name localhost;
location /api {
proxy_pass http://nginx-cluster;
}
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
- 服务器的nginx配置 , nginx地址 192.168.111.110:8081 ,
反向代理到 win机的 tomcat 的 192.168.111.1:8081 和 192.168.111.1:8082
conf文件位置 /usr/local/openresty/nginx/conf/nginx.conf
- 需要搭载 lua模块和c模块
- 共享字典: 开启nginx本地缓存 , 使用的是键值对显示缓存
- server 里 , 使用 8081端口 , 监听请求 : /item 还有 /api/item/(\d+) 和 /
- /api/item/(\d+) ,对于该请求路径, 我们进行特殊配置, 指明 配置文件的格式和位置
#user nobody;
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
#lua 模块
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
#c模块
lua_package_cpath "/usr/local/openresty/lualib/?.so;;";
# 共享字典,也就是本地缓存,名称叫做:item_cache,大小150m
lua_shared_dict item_cache 150m;
upstream tomcat-cluster { # 这里的地址 是 win机的 tomcat地址 , 是 192.168.111.1
hash $request_uri;
server 192.168.111.1:8081;
server 192.168.111.1:8082;
}
server {
listen 8081;
server_name localhost;
location /item { # 将 /item 请求 反向代理 到 Windows 的 tomcat里
proxy_pass http://tomcat-cluster;
}
location ~ /api/item/(\d+) { # 匹配一个正则表达式的路径,item后面的路径意义是 : 匹配大于等于一个字符的数字 , 也就是我们要取的id ,这样可以传进lua里
#配置对 /api/item 下的请求的 默认的响应配置
default_type application/json;
#响应结果 由lua/item.lua文件来决定, 我们就是在这里编写lua脚本查数据库
content_by_lua_file lua/item.lua;
}
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
- 现在看 /api/item/(\d+) ,对于该请求路径, 我们进行特殊配置, 该 配置文件的位置是
位置是 /usr/local/openresty/nginx/lua/item.lua
- 导入common库 , cjson, 共享字典
- 这个lua脚本 , 封装了查询逻辑 , 从 nginx-->redis-->tomcat , 这样一个顺序
- 查什么 ? 查的是 item和stock , 这个 点点 是 拼接字符串
-- 查询商品信息
local itemJSON = read_data("item:id:" .. id, 1800, "/item/" .. id, nil)
-- 查询库存信息
local stockJSON = read_data("item:stock:id:" .. id, 60, "/item/stock/" .. id, nil)
====================================================================
-- 导入common函数库
local common = require('common')
local read_http = common.read_http
local read_redis = common.read_redis
-- 导入cjson库
local cjson = require('cjson')
-- 导入共享词典,本地缓存
local item_cache = ngx.shared.item_cache
-- 封装查询函数
function read_data(key, expire, path, params)
-- 查询本地缓存
local val = item_cache:get(key)
if not val then
ngx.log(ngx.ERR, "本地缓存查询失败,尝试查询Redis, key: ", key)
-- 查询redis
val = read_redis("127.0.0.1", 6380, key)
-- 判断查询结果
if not val then
ngx.log(ngx.ERR, "redis查询失败,尝试查询http, key: ", key)
-- redis查询失败,去查询http
val = read_http(path, params)
end
end
-- 查询成功,把数据写入本地缓存
item_cache:set(key, val, expire)
-- 返回数据
return val
end
-- 获取路径参数
local id = ngx.var[1]
-- 查询商品信息
local itemJSON = read_data("item:id:" .. id, 1800, "/item/" .. id, nil)
-- 查询库存信息
local stockJSON = read_data("item:stock:id:" .. id, 60, "/item/stock/" .. id, nil)
-- JSON转化为lua的table
local item = cjson.decode(itemJSON)
local stock = cjson.decode(stockJSON)
-- 组合数据
item.stock = stock.stock
item.sold = stock.sold
-- 把item序列化为json 返回结果
ngx.say(cjson.encode(item))
- 我们在common.lua里 , 封装了 由nginx 查询 redis的 基本逻辑
文件位置 /usr/local/openresty/lualib/common.lua
- 连接redis使用的是连接池
- 获取redis连接 -->
封装查询redis的函数 (read_redis 这个函数就在 item.lua里被调用了) -->
还封装了 以http请求 , 查询 tomcat 控制器方法的 函数
-- 导入redis
local redis = require('resty.redis')
-- 初始化redis
local red = redis:new()
red:set_timeouts(1000, 1000, 1000)
-- 关闭redis连接的工具方法,其实是放入连接池
local function close_redis(red)
local pool_max_idle_time = 10000 -- 连接的空闲时间,单位是毫秒
local pool_size = 100 --连接池大小
local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
if not ok then
ngx.log(ngx.ERR, "放入redis连接池失败: ", err)
end
end
-- 查询redis的方法 ip和port是redis地址,key是查询的key
local function read_redis(ip, port, key)
-- 获取一个连接
local ok, err = red:connect(ip, port)
if not ok then
ngx.log(ngx.ERR, "连接redis失败 : ", err)
return nil
end
-- 查询redis
local resp, err = red:get(key)
-- 查询失败处理
if not resp then
ngx.log(ngx.ERR, "查询Redis失败: ", err, ", key = " , key)
end
--得到的数据为空处理
if resp == ngx.null then
resp = nil
ngx.log(ngx.ERR, "查询Redis数据为空, key = ", key)
end
close_redis(red)
return resp
end
-- 封装函数,发送http请求,并解析响应
local function read_http(path, params)
local resp = ngx.location.capture(path,{
method = ngx.HTTP_GET,
args = params,
})
if not resp then
-- 记录错误信息,返回404
ngx.log(ngx.ERR, "http查询失败, path: ", path , ", args: ", args)
ngx.exit(404)
end
return resp.body
end
-- 将方法导出
local _M = {
read_http = read_http,
read_redis = read_redis
}
return _M