本章目标
- 掌握多级缓存的定义
- 掌握redis缓存,本地缓存:不同应用场景的使用,以及他们的优劣
- 掌握热点nginx lua缓存
缓存设计原则
- 用快速存取设备,用内存
- 将缓存推到离用户最近的地方
- 脏缓存清理
补:
1.为什么将缓存推到离用户最近的地方?
现在有:前端的H5,nginx的反向代理,有miaoshaserver的Tomcat应用服务器,以及对应的数据库的mysql。缓存应该建在哪些地方呢?原则就是将缓存推到离用户最近的地方。==缓存离用户越近,用户对应访问的数据走的链路也就越少,对应的查询效率也就越高。==
2.一旦数据库里面的数据发送变化,那么该数据在缓存中就成了脏数据
多机缓存
- redis缓存
- 热点内存本地缓存
- nginx proxy cache缓存
- nginx lua 缓存
补:
==热点内存本地缓存:redis毕竟是有网络对应的开销,可以将热点数据做到jvm内存的本地缓存中==
redis缓存
redis具有存储数据库的能力,但是需要允许一定数量数据丢失的。在我们做redis使用的时候,我们其实很少特别关心数据的完整性问题,经常还会把它当做一个易失性的集中式缓存中间件并且基于KV内存级别的存储。
redis定位
redis使用
单机
1.单个机子有内存上限
2.单点故障问题。单机redis服务器出现故障,整个应用程序就不能用了。比如将token存储在redis中的用户登录操作
sentinal哨兵模式
集群cluster模式
redis cluster数据同步+paxos竞争算法
商品详情动态内容实现
思路
在redis中存储对应商品ID的ItemModel
对象(JSON序列化后的)
之后的请求可以根据URL上传来的商品ID在redis中查找对应的ItemModel
对象,减少了3次mysql查询操作,加快查询效率
优化前
ItemController.class
//商品详情页浏览
@RequestMapping(value = "/get",method = {RequestMethod.GET})
@ResponseBody
public CommonReturnType getItem(@RequestParam(name = "id")Integer id){
ItemModel itemModel = itemService.getItemById(id);
ItemVO itemVO = convertVOFromModel(itemModel);
return CommonReturnType.create(itemVO);
}
ItemServiceImpl.class
@Override
public ItemModel getItemById(Integer id) {
ItemDO itemDO = itemDOMapper.selectByPrimaryKey(id);
if(itemDO == null){
return null;
}
//操作获得库存数量
ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(itemDO.getId());
//将dataobject->model
ItemModel itemModel = convertModelFromDataObject(itemDO,itemStockDO);
//获取活动商品信息
PromoModel promoModel = promoService.getPromoByItemId(itemModel.getId());
if(promoModel != null && promoModel.getStatus().intValue() != 3){
itemModel.setPromoModel(promoModel);
}
return itemModel;
}
有三次数据库操作:1.获得商品基本信息 2.获得商品库存信息 3.获得商品活动信息
优化后
优化点:直接在Controller层缓存商品信息,不走itemService.getItemById(id);
操作去查询数据库
//商品详情页浏览
@RequestMapping(value = "/get",method = {RequestMethod.GET})
@ResponseBody
public CommonReturnType getItem(@RequestParam(name = "id")Integer id){
//根据商品的id到redis内获取
ItemModel itemModel= (ItemModel) redisTemplate.opsForValue().get("item_"+id);
//若redis内存不存在对应的itemModel,则访问下游service
if (itemModel==null){
itemModel = itemService.getItemById(id);
//设置itemModel到redis内
redisTemplate.opsForValue().set("item_"+id,itemModel);
//设置键值对的失效时间,10分钟
redisTemplate.expire("item_"+id,10, TimeUnit.MINUTES);
}
ItemVO itemVO = convertVOFromModel(itemModel);
return CommonReturnType.create(itemVO);
}
实验
1.输入http://localhost:8090/item/get?id=6
2.返回
status "success"
data
id 6
title "iphone99"
price 800
stock 60
description "最好用的苹果手机"
sales 152
imgUrl "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3974550569,4161544558&fm=27&gp=0.jpg"
promoStatus 2
promoPrice 100
promoId 1
startDate "2018-01-19 00:04:30"
3.redis-server
202.117.35.220:6379> SELECT 10
OK
202.117.35.220:6379[10]> KEYS *
1) "\xac\xed\x00\x05t\x00\x06item_6"
202.117.35.220:6379[10]> get "\xac\xed\x00\x05t\x00\x06item_6"
"\xac\xed\x00\x05sr\x000com.imooc.miaoshaproject.service.model.ItemModeln\x88\x87\xf0:\xa5i\xdd\x02\x00\bL\x00\x0bdescriptiont\x00\x12Ljava/lang/String;L\x00\x02idt\x00\x13Ljava/lang/Integer;L\x00\x06imgUrlq\x00~\x00\x01L\x00\x05pricet\x00\x16Ljava/math/BigDecimal;L\x00\npromoModelt\x003Lcom/imooc/miaoshaproject/service/model/PromoModel;L\x00\x05salesq\x00~\x00\x02L\x00\x05stockq\x00~\x00\x02L\x00\x05titleq\x00~\x00\x01xpt\x00\x18\xe6\x9c\x80\xe5\xa5\xbd\xe7\x94\xa8\xe7\x9a\x84\xe8\x8b\xb9\xe6\x9e\x9c\xe6\x89\x8b\xe6\x9c\xbasr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x06t\x00^https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3974550569,4161544558&fm=27&gp=0.jpgsr\x00\x14java.math.BigDecimalT\xc7\x15W\xf9\x81(O\x03\x00\x02I\x00\x05scaleL\x00\x06intValt\x00\x16Ljava/math/BigInteger;xq\x00~\x00\b\x00\x00\x00\x00sr\x00\x14java.math.BigInteger\x8c\xfc\x9f\x1f\xa9;\xfb\x1d\x03\x00\x06I\x00\bbitCountI\x00\tbitLengthI\x00\x13firstNonzeroByteNumI\x00\x0clowestSetBitI\x00\x06signum[\x00\tmagnitudet\x00\x02[Bxq\x00~\x00\b\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xfe\x00\x00\x00\x01ur\x00\x02[B\xac\xf3\x17\xf8\x06\bT\xe0\x02\x00\x00xp\x00\x00\x00\x02\x03 xxsr\x001com.imooc.miaoshaproject.service.model.PromoModelC$\x13\x1f\x19\xa6\xe2\xa0\x02\x00\aL\x00\aendDatet\x00\x18Lorg/joda/time/DateTime;L\x00\x02idq\x00~\x00\x02L\x00\x06itemIdq\x00~\x00\x02L\x00\x0epromoItemPriceq\x00~\x00\x03L\x00\tpromoNameq\x00~\x00\x01L\x00\tstartDateq\x00~\x00\x14L\x00\x06statusq\x00~\x00\x02xpsr\x00\x16org.joda.time.DateTime\xb8<xdj[\xdd\xf9\x02\x00\x00xr\x00\x1forg.joda.time.base.BaseDateTime\xff\xff\xf9\xe1O].\xa3\x02\x00\x02J\x00\aiMillisL\x00\x0biChronologyt\x00\x1aLorg/joda/time/Chronology;xp\x00\x00\x01oW\x89\x18\x00sr\x00'org.joda.time.chrono.ISOChronology$Stub\xa9\xc8\x11fq7P'\x03\x00\x00xpsr\x00\x1forg.joda.time.DateTimeZone$Stub\xa6/\x01\x9a|2\x1a\xe3\x03\x00\x00xpw\x0f\x00\rAsia/Shanghaixxsq\x00~\x00\a\x00\x00\x00\x01q\x00~\x00\tsq\x00~\x00\x0b\x00\x00\x00\x00sq\x00~\x00\x0e\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xfe\x00\x00\x00\x01uq\x00~\x00\x11\x00\x00\x00\x01dxxt\x00\x13iphone4\xe6\x8a\xa2\xe8\xb4\xad\xe6\xb4\xbb\xe5\x8a\xa8sq\x00~\x00\x16\x00\x00\x01a\n\x03\xb2\xb0q\x00~\x00\x1bsq\x00~\x00\a\x00\x00\x00\x02sq\x00~\x00\a\x00\x00\x00\x98sq\x00~\x00\a\x00\x00\x00<t\x00\biphone99"
采用的是java序列化方式
坑
对应的ItemModel
需要implements Serializable
对应的PromoModel
需要 implements Serializable
序列化优化
1.将redis中的KV做指定序列化处理
key做String序列化,value做json序列化处理
RedisConfig.class
@Component
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)//默认是1800
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate=new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//首先解决key的序列化方式,,序列化String
StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
//解决value的序列化方式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer(Object.class);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
实验
202.117.35.220:6379[10]> KEYS *
1) "item_6"
202.117.35.220:6379[10]> get item_6
"{\"id\":6,\"title\":\"iphone99\",\"price\":800,\"stock\":60,\"description\":\"\xe6\x9c\x80\xe5\xa5\xbd\xe7\x94\xa8\xe7\x9a\x84\xe8\x8b\xb9\xe6\x9e\x9c\xe6\x89\x8b\xe6\x9c\xba\",\"sales\":152,\"imgUrl\":\"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3974550569,4161544558&fm=27&gp=0.jpg\",\"promoModel\":{\"id\":1,\"status\":2,\"promoName\":\"iphone4\xe6\x8a\xa2\xe8\xb4\xad\xe6\xb4\xbb\xe5\x8a\xa8\",\"startDate\":{\"secondOfDay\":270,\"minuteOfDay\":4,\"centuryOfEra\":20,\"yearOfEra\":2018,\"yearOfCentury\":18,\"weekyear\":2018,\"monthOfYear\":1,\"weekOfWeekyear\":3,\"hourOfDay\":0,\"minuteOfHour\":4,\"secondOfMinute\":30,\"millisOfSecond\":0,\"millisOfDay\":270000,\"dayOfMonth\":19,\"dayOfWeek\":5,\"era\":1,\"dayOfYear\":19,\"year\":2018,\"chronology\":{\"zone\":{\"fixed\":false,\"uncachedZone\":{\"cachable\":true,\"fixed\":false,\"id\":\"Asia/Shanghai\"},\"id\":\"Asia/Shanghai\"}},\"zone\":{\"fixed\":false,\"uncachedZone\":{\"cachable\":true,\"fixed\":false,\"id\":\"Asia/Shanghai\"},\"id\":\"Asia/Shanghai\"},\"millis\":1516291470000,\"afterNow\":false,\"beforeNow\":true,\"equalNow\":false},\"endDate\":{\"secondOfDay\":0,\"minuteOfDay\":0,\"centuryOfEra\":20,\"yearOfEra\":2019,\"yearOfCentury\":19,\"weekyear\":2020,\"monthOfYear\":12,\"weekOfWeekyear\":1,\"hourOfDay\":0,\"minuteOfHour\":0,\"secondOfMinute\":0,\"millisOfSecond\":0,\"millisOfDay\":0,\"dayOfMonth\":31,\"dayOfWeek\":2,\"era\":1,\"dayOfYear\":365,\"year\":2019,\"chronology\":{\"zone\":{\"fixed\":false,\"uncachedZone\":{\"cachable\":true,\"fixed\":false,\"id\":\"Asia/Shanghai\"},\"id\":\"Asia/Shanghai\"}},\"zone\":{\"fixed\":false,\"uncachedZone\":{\"cachable\":true,\"fixed\":false,\"id\":\"Asia/Shanghai\"},\"id\":\"Asia/Shanghai\"},\"millis\":1577721600000,\"afterNow\":true,\"beforeNow\":false,\"equalNow\":false},\"itemId\":6,\"promoItemPrice\":100}}"
结果还是不好,进一步需要对PromoModel
中的Datetime
进行序列化
2.对Datetime进行json序列化
RedisConfig.class
@Component
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)//默认是1800
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate=new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//首先解决key的序列化方式,,序列化String
StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
//解决value的序列化方式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer(Object.class);
//序列化Datetime
ObjectMapper objectMapper=new ObjectMapper();
SimpleModule simpleModule=new SimpleModule();
simpleModule.addSerializer(DateTime.class,new JodaDatimeJsonSerializer());
simpleModule.addDeserializer(DateTime.class,new JodaDateTimeJsonDeserializer());
objectMapper.registerModule(simpleModule);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
结果:
202.117.35.220:6379[10]> GEt item_6
"{\"id\":6,\"title\":\"iphone99\",\"price\":800,\"stock\":60,\"description\":\"\xe6\x9c\x80\xe5\xa5\xbd\xe7\x94\xa8\xe7\x9a\x84\xe8\x8b\xb9\xe6\x9e\x9c\xe6\x89\x8b\xe6\x9c\xba\",\"sales\":152,\"imgUrl\":\"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3974550569,4161544558&fm=27&gp=0.jpg\",\"promoModel\":{\"id\":1,\"status\":2,\"promoName\":\"iphone4\xe6\x8a\xa2\xe8\xb4\xad\xe6\xb4\xbb\xe5\x8a\xa8\",\"startDate\":\"2018-01-19 00:04:30\",\"endDate\":\"2019-12-31 00:00:00\",\"itemId\":6,\"promoItemPrice\":100}}"
3.在redis中value值里如类信息
RedisConfig.class
//在value中加入对应类的信息
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
最终结果:
202.117.35.220:6379[10]> GEt item_6
"[\"com.imooc.miaoshaproject.service.model.ItemModel\",{\"id\":6,\"title\":\"iphone99\",\"price\":[\"java.math.BigDecimal\",800],\"stock\":60,\"description\":\"\xe6\x9c\x80\xe5\xa5\xbd\xe7\x94\xa8\xe7\x9a\x84\xe8\x8b\xb9\xe6\x9e\x9c\xe6\x89\x8b\xe6\x9c\xba\",\"sales\":152,\"imgUrl\":\"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3974550569,4161544558&fm=27&gp=0.jpg\",\"promoModel\":[\"com.imooc.miaoshaproject.service.model.PromoModel\",{\"id\":1,\"status\":2,\"promoName\":\"iphone4\xe6\x8a\xa2\xe8\xb4\xad\xe6\xb4\xbb\xe5\x8a\xa8\",\"startDate\":\"2018-01-19 00:04:30\",\"endDate\":\"2019-12-31 00:00:00\",\"itemId\":6,\"promoItemPrice\":[\"java.math.BigDecimal\",100]}]}]"
redis集中式缓存压测效果验证
本地热点缓存
- 存放热点数据
- 脏读非常不敏感
- 内存可控
注:
本地缓存其实就是==java虚拟机JVM的缓存==
Guava cache
单纯的hashmap是无法满足的
1.并发读写
2.内存淘汰机制
介绍 Guava
- 可控制的大小和超时时间
- 可配置的lru策略
- 线程安全
思路
因为我们是多机缓存,先找1.本地缓存
,不存在2.找redis
,还不存在3.找mysql数据库
。
代码
引入jar包
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
ItemController.class
//商品详情页浏览
@RequestMapping(value = "/get",method = {RequestMethod.GET})
@ResponseBody
public CommonReturnType getItem(@RequestParam(name = "id")Integer id){
ItemModel itemModel=null;
//先取本地缓存
itemModel=(ItemModel)cacheService.getFromCommonCache("item_"+id);
//本地缓存没有,找redis
if (itemModel==null){
//根据商品的id到redis内获取
itemModel= (ItemModel) redisTemplate.opsForValue().get("item_"+id);
//若redis内存不存在对应的itemModel,则访问下游itemService查询数据库
if (itemModel==null){
itemModel = itemService.getItemById(id);
//设置itemModel到redis内
redisTemplate.opsForValue().set("item_"+id,itemModel);
//设置键值对的失效时间,10分钟
redisTemplate.expire("item_"+id,10, TimeUnit.MINUTES);
}
//填充本地缓存
cacheService.setCommonCache("item_"+id,itemModel);
}
ItemVO itemVO = convertVOFromModel(itemModel);
return CommonReturnType.create(itemVO);
}
本地缓存缺点
1.当本地热点缓存中数据发生更新时,本地热点缓存没有好的机制去更新
2.本地热点缓存还有一个JVM容量大小的限制
nginx缓存
本地缓存:
1.nginx将用户请求分发到miaosha.jar
中,并经过org.springframework.web.servlet. DispatcherServlet
中转发,需要启动java代码完成对应的操作,总规是有消耗
2.nginx和miaosha.jar直接的局域网连接
试想一下,我们是不是可以把缓存在nginx上做?
因为nginx是里用户H5最近的一个节点,若我们把对应的热点数据做到nginx上的话,优化的策略会更好。
1.nginx proxy cache缓存
- nginx反向代理前置
- 依靠文件系统存对应索引的文件
- 依靠内存缓存文件地址
修改nginx.conf
#申明一个cache缓存节点的内容
proxy_cache_path /usr/local/Cellar/openresty/1.15.8.1/nginx/tmp_cache levels=1:2 keys_zone=tmp_cache:100m inactive=7d max_size=10g;
location / {
proxy_pass http://backend_server;
#nginx proxy cache
proxy_cache tmp_cache;
proxy_cache_key $uri;
proxy_cache_valid 200 206 304 302 7d;
proxy_set_header Host $http_host:$proxy_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
实验
在浏览器输入http://localhost/item/get?id=6
如果配置成功,linux服务器的/xxh009/miaosha/tomcat/access_log.2019-06-23.log
里,多次刷新页面,也只有一行记录
115.154.255.245 - - [23/Jun/2019:19:22:02 +0800] "GET /item/get?id=6 HTTP/1.1" 200 328 37
并且在/usr/local/Cellar/openresty/1.15.8.1/nginx/tmp_cache/中有对应的缓存文件
比如
xuxinghua@xxhdemac /usr/local/Cellar/openresty/1.15.8.1/nginx/tmp_cache/8/f6 cat 86e4d1b3ba4f1464e409c74be4ef6f68
�]��������c`]ksr�`�
KEY: /item/get
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 23 Jun 2019 11:22:02 GMT
{"status":"success","data":{"id":6,"title":"iphone99","price":800,"stock":60,"description":"最好用的苹果手机","sales":152,"imgUrl":"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3974550569,4161544558&fm=27&gp=0.jpg","promoStatus":2,"promoPrice":100,"promoId":1,"startDate":"2018-01-19 00:04:30"}}%
压测及结果分析
压测完后,其实还没有之前的速度快,为什么呢?
因为我们做的这个nginx proxy cache的数据是存放在本地磁盘的,读写磁盘效率很低,还不如nginx与miaosha.jar直接的网络传输效率高。
所以不用nginx proxy cache。
2.nginx lua
- lua协程机制
- nginx协程机制
- nginx lua插载点
- OpenResty
协程机制
- 依附于线程的内存模型,切换开销小
- 遇阻塞及归还执行权,代码同步
- 无需加锁
nginx协程
- nginx的每一个Worker进程都是在epoll或kqueue这种事件模型之上,封装成协程
- 每一个请求都有一个协程进行处理
- 即使ngx_lua需要运行lua,相对C有一定的开销,但依旧能保证高并发能力
nginx协程机制
- nginx每个工作进程创建一个lua虚拟机
- 工作进程内的所有协程共享同一个vm
- 每一个外部请求由一个lua协程处理,之间数据隔离
- lua代码调用io等异步接口时,协程被挂起,保存上下文数据保持不变
- 自动保持,不阻塞工作进程
- io异步操作完成后还有协程上下文,代码继续执行
nginx处理阶段
*_handler 用户可以定制
nginx lua插载点
- Init_by_lua:系统启动时调用
- init_worker_by_lua:worker进程启动时调用
- set_by_lua:nginx变量使用复杂lua return
- rewrite_by_lua:重写url规则
- access_by_lua:权限验证阶段
- content_by_lua:内容输出节点
content_by_lua:内容输出节点
目的
拦截url为/staticitem/get
的请求
实现
1.在/usr/local/Cellar/openresty/1.15.8.1/lua/
目录下新建staticitem.lua脚本
staticitem.lua
ngx.say("hello static item lua");
意思就是:
以HTTP response的形式返回字符串"hello static item lua"
2.修改nginx.conf
location /staticitem/get {
content_by_lua_file /usr/local/Cellar/openresty/1.15.8.1/lua/staticitem.lua;
}
实验
好处
nginx可以拦截nginx.conf中的location请求,将url路由到我们配置lua脚本做相应的处理。比如上面的例子中nginx就可以将/staticitem/get
请求对应到我们编写的taticitem.lua,最终向浏览器返回hello static item lua
OpenResty
- OpenResty由Nginx核心+很多第三方模块组成,默认集成Lua开发环境,使得Nginx可以作为一个Web Server使用。
- 借助于Nginx的事件驱动模型和非阻塞IO,可以实现高性能的Web应用程序
- OpenResty提供了大量组件如mysql、redis、memcached等等,使在nginx上开发Web应用程序更方便更简单
实战
- OpenResty hello world
- shared dic:共享内存字典,所有进程可见,lru淘汰(替换到nginx自己的基于文件系统的proxy cache)
- OpenResty对redis的支持
OpenResty hello world
- 在
/usr/local/Cellar/openresty/1.15.8.1/lua/
目录下创建helloworld.lua脚本
ngx.exec("/item/get?id=6");
修改nginx.conf配置
#openresty helloworld location /helloworld{ content_by_lua_file /usr/local/Cellar/openresty/1.15.8.1/lua/helloworld.lua; }
实验
显示:
shared dic
在
/usr/local/Cellar/openresty/1.15.8.1/lua/
目录下创建itemshareddic.lua脚本function get_from_cache(key) local cache_ngx=ngx.shared.my_cache local value=cache_ngx:get(key) return value end function set_to_cache(key,value,exptime) if not exptime then exptime =0 end local cache_ngx=ngx.shared.my_cache local succ,err,forcible=cache_ngx:set(key,value,exptime) return succ end local args=ngx.req.get_uri_args(); local id=args["id"] local item_model=get_from_cache("item_"..id); if item_model == nil then local resp=ngx.location.capture("/item/get?id="..id) item_model = resp.body set_to_cache("item_"..id,item_model,1*60) end ngx.say(item_model)
修改nginx.conf
location /luaitem/get { default_type "application/json"; content_by_lua_file /usr/local/Cellar/openresty/1.15.8.1/lua/itemshareddic.lua; }
实验
OpenResty对redis的支持
在
/usr/local/Cellar/openresty/1.15.8.1/lua/
目录下创建itemredi.lua脚本local args=ngx.req.get_uri_args() local id=args["id"] local redis= require "resty.redis" local cache=redis:new() local ok,err= cache:connect("localhost",6379) local item_model=cache:get("item_"..id) if item_model==ngx.null or item_model==nil then local resp=ngx.location.capture("/item/get?id="..id) item_model=resp.body end ngx.say(item_model)
修改nginx.conf
location /luaitem/get { default_type "application/json"; content_by_lua_file /usr/local/Cellar/openresty/1.15.8.1/lua/itemredis.lua; }
实验