1、安装docker教程:黑马-Centos7安装Docker
2、根据本章资料中【案例导入说明.md】启动mysql镜像,注意要先关闭本地的mysql服务,否则3306端口被占用,mysql镜像会启动失败。
3、接下来完全按照【案例导入说明.md】导入
按照【安装OpenResty】教程安装,云服务器记得打开配置文件中对应端口(8081)
本地访问商品详情页时需要开启本地nginx,同时云服务器也要reload配置。
//服务器OpenResty的nginx.conf
#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;;";
server {
listen 8081;
server_name localhost;
location ~ /api/item/(\d+) {
# 默认的响应类型
default_type application/json;
# 响应结果由lua/item.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;
}
}
}
--OpenResty的item.lua
-- 获取路径参数
local id = ngx.var[1]
-- 返回结果
ngx.say('{"id":' .. id .. ',"name":"SALSA AIR","title":"RIMOWA 26寸托运箱拉杆箱 SALSA AIR系列果绿色 820.70.36.4","price":19900,"image":"https://m.360buyimg.com/mobilecms/s720x720_jfs/t6934/364/1195375010/84676/e9f2c55f/597ece38N0ddcbc77.jpg!q70.jpg.webp","category":"拉杆箱","brand":"RIMOWA","spec":"","status":1,"createTime":"2019-04-30T16:00:00.000+00:00","updateTime":"2019-04-30T16:00:00.000+00:00","stock":2999,"sold":31290}')
这里由于本人的OpenResty安装在云服务器,而运行的程序(即视频中说到的tomcat服务器)在本地,实操踩了很多坑,详细记录一下。
首先我要先理清请求的过程:
1、浏览器向windows本地80端口发送请求:http://localhost/api/item/10003
2、该请求被本地的nginx拦截处理,转发给:云服务器IP:8081,即云服务器OpenResty
3、云服务器OpenResty(基于nginx)监听到符合/api/item/(\d+)的请求,调用lua脚本查询数据。由于此时云服务器被反向代理到windows本地,lua脚本查询到本地8081端口的tomcat服务器中的数据。
因此,想要正确地查到数据,首先需要先保证本地程序正处于运行状态,为8081端口提供服务。
其次需要保证本地端口能被云服务器(外网)访问,因此需要对本地windows进行内网穿透,这里我使用的是花生壳。
接下来贴出我的对应配置即代码以供参考:(出于隐私保护,我将对应ip和域名替换成【解释】的形式)
1、本地nginx配置文件
#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;
#nginx的业务集群,nginx本地缓存,redis缓存,tomcat查询
upstream nginx-cluster{
server 【云服务器ip】: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;
}
}
}
2、云服务器OpenResty中nginx配置
#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;;";
server {
listen 8081;
server_name localhost;
location /item {
# windows电脑的ip和java服务端口
proxy_pass http://【本地windows内网穿透后的外网域名】:【网穿透后的端口】;
}
location ~ /api/item/(\d+) {
# 默认的响应类型
default_type application/json;
# 响应结果由lua/item.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;
}
}
}
3、云服务器OpenResty中执行的lua脚本(这里调用的 read_http 等函数可以自行去黑马资料中找)
--导入common库
local common = require('common')
local read_http = common.read_http
-- 导入cjson库
local cjson = require('cjson')
-- 获取路径参数
local id = ngx.var[1]
-- 查询商品信息
local itemJSON = read_http("/item/" .. id,nil)
-- 查询库存信息
local stockJSON = read_http("/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))
多台tomcat负载均衡案例的云服务器OpenResty中nginx配置
#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;;";
upstream tomcat-cluster {
hash $request_uri;
server 【本地windows内网穿透后的外网域名】:【网穿透后的端口1】;# 注意这里没有http://
server 【本地windows内网穿透后的外网域名】:【网穿透后的端口2】;# 注意这里没有http://
}
server {
listen 8081;
server_name localhost;
location /item {
# windows电脑的ip和java服务端口
proxy_pass http://tomcat-cluster;
}
location ~ /api/item/(\d+) {
# 默认的响应类型
default_type application/json;
# 响应结果由lua/item.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;
}
}
}
docker run --name redis -p 6379:6379 -d redis redis-server --appendonly yes
2、在item-service服务中引入Redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3、配置Redis地址
spring:
redis:
host: 【云服务器的ip(用于访问docker中6379端口的redis)】
4、编写初始化类
@Component
public class RedisHandler implements InitializingBean {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private IItemService itemService;
@Autowired
private IItemStockService stockService;
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
public void afterPropertiesSet() throws Exception {
//初始化缓存
// 1.查询商品信息
List<Item> itemList = itemService.list();
// 2.放入缓存
for (Item item : itemList) {
// 2.1.item序列化为JSON
String json = MAPPER.writeValueAsString(item);
// 2.2.存入redis
redisTemplate.opsForValue().set("item:id:" + item.getId(), json);
}
// 3.查询商品库存信息
List<ItemStock> stockList = stockService.list();
// 4.放入缓存
for (ItemStock stock : stockList) {
// 2.1.item序列化为JSON
String json = MAPPER.writeValueAsString(stock);
// 2.2.存入redis
redisTemplate.opsForValue().set("item:stock:id:" + stock.getId(), json);
}
}
}
-- 引入redis模块
local redis = require('resty.redis')
-- 初始化Redis对象
local red = redis:new()
-- 设置Redis超时时间
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
2、修改item.lua,设置为先查redis后查tomcat,减小tomcat服务器的压力,这里的127.0.0.1要替换成云服务器ip。
这里我也不清楚原因,明明OpenResty和redis都在云服务器上,但使用127.0.0.1查询就只能查到前三个商品。如果有大佬懂的话欢迎在评论区解答!
--导入common库
local common = require('common')
local read_http = common.read_http
local read_redis = common.read_redis
-- 导入cjson库
local cjson = require('cjson')
-- 封装查询函数
function read_data(key, path, params)
-- 先查redis
-- local resp = read_redis("127.0.0.1",6379,key)
local resp = read_redis("【云服务器ip】",6379,key)
-- 判断查询结果
if not resp then
-- 这里一定要加(key or nil),否则会报错lua entry thread aborted: runtime error
ngx.log("redis查询失败,尝试查询http key:",(key or nil))
-- redis查询失败,查询http
resp = read_http(path, params)
end
return resp
end
-- 获取路径参数
local id = ngx.var[1]
-- 查询商品信息
local itemJSON = read_data("item:id:" .. id,"/item/" .. id,nil)
-- 查询库存信息
local stockJSON = read_data("item:stock:id:" .. id,"/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))
1、OpenResty的配置中开启共享词典(本地缓存)
#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;;";
# 添加共享词典(本地缓存)
lua_shared_dict item_cache 150m;
upstream tomcat-cluster {
hash $request_uri;
server 【本地windows内网穿透后的外网域名】:【网穿透后的端口1】;# 注意这里没有http://
server 【本地windows内网穿透后的外网域名】:【网穿透后的端口2】;# 注意这里没有http://
}
server {
listen 8081;
server_name localhost;
location /item {
# windows电脑的ip和java服务端口
proxy_pass http://tomcat-cluster;
}
location ~ /api/item/(\d+) {
# 默认的响应类型
default_type application/json;
# 响应结果由lua/item.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;
}
}
}
2、修改item.lua中的业务逻辑
--导入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 or nil))
-- 查redis
-- val = read_redis("127.0.0.1",6379,key)
val = read_redis("124.222.157.95",6379,key)
-- 判断查询结果
if not val then
ngx.log(ngx.ERR, "redis查询失败,尝试查询http key:",(key or nil))
-- 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))
1、基于MQ的异步通知:发消息对于item-service仍然有侵入
2、基于Canal的异步通知:无侵入