基于Nginx+Lua+Java完成多级缓存架构的核心业务逻辑

发布 : 2017-07-10 分类 : 大数据 浏览 :
1
分发层nginx,lua应用,会将商品id,商品店铺id,都转发到后端的应用nginx
  • 应用nginx的lua脚本接收到请求

  • 获取请求参数中的商品id,以及商品店铺id

  • 根据商品id和商品店铺id,在nginx本地缓存中尝试获取数据

  • 如果在nginx本地缓存中没有获取到数据,那么就到redis分布式缓存中获取数据,如果获取到了数据,还要设置到nginx本地缓存中

  • 如果缓存数据生产服务没有在redis分布式缓存中没有获取到数据,那么就在自己本地ehcache中获取数据,返回数据给nginx,也要设置到nginx本地缓存中

  • 如果ehcache本地缓存都没有数据,那么就需要去原始的服务中拉去数据,该服务会从mysql中查询,拉去到数据之后,返回给nginx,并重新设置到ehcache和redis中

  • nginx最终利用获取到的数据,动态渲染网页模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@matrix-cache03 ~]# vi /usr/hello/hello.conf
server {
listen 80;
server_name _;

location /hello {
default_type 'text/html';
content_by_lua_file /usr/hello/lua/hello.lua;
}

location /product {
default_type 'text/html';
content_by_lua_file /usr/hello/lua/distribute.lua;
}
}
1
2
[root@matrix-cache03 ~]# cd /usr/hello/lua
[root@matrix-cache03 lua]# cp hello.lua distribute.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[root@matrix-cache03 ~]# vi /usr/hello/lua/distribute.lua
local uri_args = ngx.req.get_uri_args()
local productId = uri_args["productId"]
local shopId = uri_args["shopId"]

local hosts = {"192.168.31.231","192.168.31.232"}
local hash = ngx.crc32_long(productId)
local index = (hash % 1) + 1
backend = "http://"..hosts[index]

local requestPath = uri_args["requestPath"]
requestPath = "/"..requestPath.."?productId="..productId.."&shopId="..shopId

local http = require("resty.http")
local httpc = http.new()

local resp,err = httpc:request_uri(backend,{
method = "GET",
path = requestPath
})

if not resp then
ngx.say("Request error:",err)
return
end

ngx.say(resp.body)

httpc:close()

重新加载Nginx服务

1
[root@matrix-cache03 ~]# /usr/servers/nginx/sbin/nginx -s reload

在浏览器地址栏中访问

1
http://192.168.31.233/hello?requestPath=hello&productId=1

1
2
3
4
5
建议不要用nginx+lua直接去获取redis数据

因为openresty没有太好的redis cluster的支持包,所以建议是发送http请求到缓存数据生产服务,由该服务提供一个http接口

缓存数生产服务可以基于redis cluster api从redis中直接获取数据,并返回给nginx
1
2
3
[root@matrix-cache01 ~]# cd /usr/hello/lualib/resty/
[root@matrix-cache01 resty]# wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http_headers.lua
[root@matrix-cache01 resty]# wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http.lua
1
2
3
[root@matrix-cache02 ~]# cd /usr/hello/lualib/resty/
[root@matrix-cache02 resty]# wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http_headers.lua
[root@matrix-cache02 resty]# wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http.lua

渲染模板

1
2
3
4
5
[root@matrix-cache01 ~]# cd /usr/hello/lualib/resty/
[root@matrix-cache01 resty]# wget https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template.lua
[root@matrix-cache01 resty]# mkdir /usr/hello/lualib/resty/html
[root@matrix-cache01 resty]# cd /usr/hello/lualib/resty/html
[root@matrix-cache01 html]# wget https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template/html.lua
1
2
3
4
5
[root@matrix-cache02 ~]# cd /usr/hello/lualib/resty/
[root@matrix-cache02 resty]# wget https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template.lua
[root@matrix-cache02 resty]# mkdir /usr/hello/lualib/resty/html
[root@matrix-cache02 resty]# cd /usr/hello/lualib/resty/html
[root@matrix-cache02 html]# wget https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template/html.lua

在hello.conf的server中配置模板位置

1
2
[root@matrix-cache01 ~]# vi /usr/hello/hello.conf
[root@matrix-cache02 ~]# vi /usr/hello/hello.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
listen 80;
server_name _;

set $template_location "/templates";
set $template_root "/usr/hello/templates";

location /hello {
default_type 'text/html';
content_by_lua_file /usr/hello/lua/hello.lua;
}

location /product {
default_type 'text/html';
content_by_lua_file /usr/hello/lua/product.lua;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
[root@matrix-cache01 ~]# vi /usr/hello/lua/product.lua
[root@matrix-cache02 ~]# vi /usr/hello/lua/product.lua
local uri_args = ngx.req.get_uri_args()
local productId = uri_args["productId"]
local shopId = uri_args["shopId"]

local cache_ngx = ngx.shared.my_cache

local productCacheKey = "product_info_"..productId
local shopCacheKey = "shop_info_"..shopId

local productCache = cache_ngx:get(productCacheKey)
local shopCache = cache_ngx:get(shopCacheKey)

if productCache == "" or productCache == nil then
local http = require("resty.http")
local httpc = http.new()

local resp, err = httpc:request_uri("http://192.168.31.178:8080",{
method = "GET",
path = "/getProductInfo?productId="..productId
})

productCache = resp.body
cache_ngx:set(productCacheKey, productCache, 10 * 60)
end

if shopCache == "" or shopCache == nil then
local http = require("resty.http")
local httpc = http.new()

local resp, err = httpc:request_uri("http://192.168.31.178:8080",{
method = "GET",
path = "/getShopInfo?shopId="..shopId
})

shopCache = resp.body
cache_ngx:set(shopCacheKey, shopCache, 10 * 60)
end

local cjson = require("cjson")
local productCacheJSON = cjson.decode(productCache)
local shopCacheJSON = cjson.decode(shopCache)

local context = {
productId = productCacheJSON.id,
productName = productCacheJSON.name,
productPrice = productCacheJSON.price,
productPictureList = productCacheJSON.pictureList,
productSpecification = productCacheJSON.specification,
productService = productCacheJSON.service,
productColor = productCacheJSON.color,
productSize = productCacheJSON.size,
shopId = shopCacheJSON.id,
shopName = shopCacheJSON.name,
shopLevel = shopCacheJSON.level,
shopGoodCommentRate = shopCacheJSON.goodCommentRate
}

local template = require("resty.template")
template.render("product.html", context)

编辑nginx配置文件

在配置文件中添加

1
2
3
4
5
[root@matrix-cache01 ~]# vi /usr/servers/nginx/conf/nginx.conf
[root@matrix-cache02 ~]# vi /usr/servers/nginx/conf/nginx.conf
http {
lua_shared_dict my_cache 128m;
}

编辑product模板

1
2
[root@matrix-cache01 ~]# mkdir /usr/hello/templates
[root@matrix-cache02 ~]# mkdir /usr/hello/templates
1
2
3
4
[root@matrix-cache01 ~]# cd /usr/hello/templates
[root@matrix-cache02 ~]# cd /usr/hello/templates
[root@matrix-cache01 templates]# vi product.html
[root@matrix-cache02 templates]# vi product.html
1
2
3
4
5
6
7
8
9
10
11
product id: {* productId *}<br/>
product name: {* productName *}<br/>
product picture list: {* productPictureList *}<br/>
product specification: {* productSpecification *}<br/>
product service: {* productService *}<br/>
product color: {* productColor *}<br/>
product size: {* productSize *}<br/>
shop id: {* shopId *}<br/>
shop name: {* shopName *}<br/>
shop level: {* shopLevel *}<br/>
shop good cooment rate: {* shopGoodCommentRate *}<br/>

将渲染后的网页模板作为http响应,返回给分发层nginx

重新加载Nginx服务

1
2
[root@matrix-cache01 ~]# /usr/servers/nginx/sbin/nginx -s reload
[root@matrix-cache02 ~]# /usr/servers/nginx/sbin/nginx -s reload

在浏览器地址栏中输入

1
http://192.168.31.233/product?requestPath=product&productId=1&shopId=1

查看Nginx日志

1
[root@matrix-cache01 ~]# tail -f /usr/servers/nginx/logs/error.log

编写代码

缓存Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package com.matrix.eshop.cache.controller;

import javax.annotation.Resource;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.matrix.eshop.cache.model.ProductInfo;
import com.matrix.eshop.cache.model.ShopInfo;
import com.matrix.eshop.cache.service.CacheService;

/**
* 缓存Controller
*
* @author matrix
*
*/
@Controller
public class CacheController {

@Resource
private CacheService cacheService;

@RequestMapping("/testPutCache")
@ResponseBody
public String testPutCache(ProductInfo productInfo) {
cacheService.saveLocalCache(productInfo);
return "success";
}

@RequestMapping("/testGetCache")
@ResponseBody
public ProductInfo testGetCache(Long id) {
return cacheService.getLocalCache(id);
}

@RequestMapping("/getProductInfo")
@ResponseBody
public ProductInfo getProductInfo(Long productId) {
ProductInfo productInfo = null;
// 先从Redis中获取数据
productInfo = cacheService.getProductInfoFromReidsCache(productId);
System.out.println("=================从redis中获取缓存,商品信息=" + productInfo);

if (productInfo == null) {
productInfo = cacheService.getProductInfoFromLocalCache(productId);
System.out.println("=================从ehcache中获取缓存,商品信息=" + productInfo);
}

if (productInfo == null) {
// 需要从数据源重新拉取数据,重建缓存
}
return productInfo;
}

@RequestMapping("/getShopInfo")
@ResponseBody
public ShopInfo getShopInfo(Long shopId) {
ShopInfo shopInfo = null;
// 先从Redis中获取数据
shopInfo = cacheService.getShopInfoFromReidsCache(shopId);
System.out.println("=================从redis中获取缓存,店铺信息=" + shopInfo);

if (shopInfo == null) {
shopInfo = cacheService.getShopInfoFromLocalCache(shopId);
System.out.println("=================从ehcache中获取缓存,店铺信息=" + shopInfo);
}

if (shopInfo == null) {
// 需要从数据源重新拉取数据,重建缓存
}
return shopInfo;
}
}

缓存service接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.matrix.eshop.cache.service;

import com.matrix.eshop.cache.model.ProductInfo;
import com.matrix.eshop.cache.model.ShopInfo;

/**
* 缓存service接口
* @author matrix
*
*/
public interface CacheService {

/**
* 将商品信息保存到本地缓存中
* @param productInfo
* @return
*/
public ProductInfo saveLocalCache(ProductInfo productInfo);

/**
* 从本地缓存中获取商品信息
* @param id
* @return
*/
public ProductInfo getLocalCache(Long id);

/**
* 将商品信息保存到本地的ehcache缓存中
* @param productInfo
*/
public ProductInfo saveProductInfo2LocalCache(ProductInfo productInfo);

/**
* 从本地ehcache缓存中获取商品信息
* @param productId
* @return
*/
public ProductInfo getProductInfoFromLocalCache(Long productId);

/**
* 将店铺信息保存到本地的ehcache缓存中
* @param productInfo
*/
public ShopInfo saveShopInfo2LocalCache(ShopInfo shopInfo);

/**
* 从本地ehcache缓存中获取店铺信息
* @param productId
* @return
*/
public ShopInfo getShopInfoFromLocalCache(Long shopId);

/**
* 将商品信息保存到redis中
* @param productInfo
*/
public void saveProductInfo2ReidsCache(ProductInfo productInfo);

/**
* 将店铺信息保存到redis中
* @param productInfo
*/
public void saveShopInfo2ReidsCache(ShopInfo shopInfo);

/**
* 从Redis中获取商品信息
* @param productInfo
*/
public ProductInfo getProductInfoFromReidsCache(Long productId);

/**
* 从Redis中获取商品店铺的信息
* @param productInfo
*/
public ShopInfo getShopInfoFromReidsCache(Long shopId);
}

缓存Service实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package com.matrix.eshop.cache.service.impl;

import javax.annotation.Resource;

import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.matrix.eshop.cache.model.ProductInfo;
import com.matrix.eshop.cache.model.ShopInfo;
import com.matrix.eshop.cache.service.CacheService;

import redis.clients.jedis.JedisCluster;

/**
* 缓存Service实现类
*
* @author matrix
*
*/
@Service("cacheService")
public class CacheServiceImpl implements CacheService {

public static final String CACHE_NAME = "local";

@Resource
private JedisCluster jedisCluster;

/**
* 将商品信息保存到本地缓存中
*
* @param productInfo
* @return
*/
@CachePut(value = CACHE_NAME, key = "'key_'+#productInfo.getId()")
public ProductInfo saveLocalCache(ProductInfo productInfo) {
return productInfo;
}

/**
* 从本地缓存中获取商品信息
*
* @param id
* @return
*/
@Cacheable(value = CACHE_NAME, key = "'key_'+#id")
public ProductInfo getLocalCache(Long id) {
return null;
}

/**
* 将商品信息保存到本地的ehcache缓存中
*
* @param productInfo
*/
@CachePut(value = CACHE_NAME, key = "'product_info_'+#productInfo.getId()")
public ProductInfo saveProductInfo2LocalCache(ProductInfo productInfo) {
return productInfo;
}

/**
* 从本地ehcache缓存中获取商品信息
*
* @param productId
* @return
*/
@Cacheable(value = CACHE_NAME, key = "'product_info_'+#productId")
public ProductInfo getProductInfoFromLocalCache(Long productId) {
return null;
}

/**
* 将店铺信息保存到本地的ehcache缓存中
*
* @param productInfo
*/
@CachePut(value = CACHE_NAME, key = "'shop_info_'+#shopInfo.getId()")
public ShopInfo saveShopInfo2LocalCache(ShopInfo shopInfo) {
return shopInfo;
}

/**
* 从本地ehcache缓存中获取店铺信息
*
* @param productId
* @return
*/
@Cacheable(value = CACHE_NAME, key = "'shop_info_'+#shopId")
public ShopInfo getShopInfoFromLocalCache(Long shopId) {
return null;
}

/**
* 将商品信息保存到redis中
*
* @param productInfo
*/
public void saveProductInfo2ReidsCache(ProductInfo productInfo) {
String key = "product_info_" + productInfo.getId();
jedisCluster.set(key, JSONObject.toJSONString(productInfo));
}

/**
* 将店铺信息保存到redis中
*
* @param productInfo
*/
public void saveShopInfo2ReidsCache(ShopInfo shopInfo) {
String key = "shop_info_" + shopInfo.getId();
jedisCluster.set(key, JSONObject.toJSONString(shopInfo));
}

@Override
public ProductInfo getProductInfoFromReidsCache(Long productId) {
String key = "product_info_" + productId;
String json = jedisCluster.get(key);
return JSONObject.parseObject(json, ProductInfo.class);
}

@Override
public ShopInfo getShopInfoFromReidsCache(Long shopId) {
String key = "shop_info_" + shopId;
String json = jedisCluster.get(key);
return JSONObject.parseObject(json, ShopInfo.class);
}

}

查看本地服务

1
http://192.168.31.178:8080/getProductInfo?productId=1

1
http://192.168.31.178:8080/getShopInfo?shopId=1

本文作者 : Matrix
原文链接 : https://matrixsparse.github.io/2017/07/10/基于Nginx+Lua+Java完成多级缓存架构的核心业务逻辑/
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

知识 & 情怀 | 二者兼得

微信扫一扫, 向我投食

微信扫一扫, 向我投食

支付宝扫一扫, 向我投食

支付宝扫一扫, 向我投食

留下足迹