第5章 查询优化技术之多级缓存

本章目标

  • 掌握多级缓存的定义
  • 掌握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哨兵模式

未命名文件-2

集群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集中式缓存压测效果验证

屏幕快照 2019-06-23 下午2.03.04

本地热点缓存

  • 存放热点数据
  • 脏读非常不敏感
  • 内存可控

注:

本地缓存其实就是==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处理阶段

屏幕快照 2019-06-23 下午8.04.21

*_handler 用户可以定制

nginx lua插载点

屏幕快照 2019-06-23 下午8.07.26

  1. Init_by_lua:系统启动时调用
  2. init_worker_by_lua:worker进程启动时调用
  3. set_by_lua:nginx变量使用复杂lua return
  4. rewrite_by_lua:重写url规则
  5. access_by_lua:权限验证阶段
  6. 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;

}
实验

屏幕快照 2019-06-23 下午8.35.18

好处

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
  1. /usr/local/Cellar/openresty/1.15.8.1/lua/目录下创建helloworld.lua脚本
ngx.exec("/item/get?id=6");
  1. 修改nginx.conf配置

    #openresty helloworld
    location /helloworld{
    content_by_lua_file /usr/local/Cellar/openresty/1.15.8.1/lua/helloworld.lua;
    }
  2. 实验

输入http://localhost/helloworld

显示:

11

shared dic
  1. /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)
  2. 修改nginx.conf

    location /luaitem/get {
    default_type "application/json";
    content_by_lua_file /usr/local/Cellar/openresty/1.15.8.1/lua/itemshareddic.lua;
    }
  3. 实验

    输入http://localhost/luaitem/get?id=6

    实验图

OpenResty对redis的支持

openresty对redis支持

  1. /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)
  2. 修改nginx.conf

    location /luaitem/get {
    default_type "application/json";
    content_by_lua_file /usr/local/Cellar/openresty/1.15.8.1/lua/itemredis.lua;
    }
  3. 实验

    输入http://localhost/luaitem/get?id=6

    实验图


   转载规则


《第5章 查询优化技术之多级缓存》 徐兴华 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
第6章 查询优化技术之页面静态化 第6章 查询优化技术之页面静态化
目标 优化的是H5(static),请求走静态资源文件。 将静态资源的请求路由到CDN 静态请求CDN 结构图 DNS用CNAME解析源站回源缓存设置cache control响应头Request Headers: 例: GE
2019-06-18
下一篇 
第4章 分布式扩展 第4章 分布式扩展
[TOC] 本章目标 nginx反向代理负载均衡 分布式会话管理 使用redis实现分布式会话存储 单机部署结构: #nginx反向代理负载均衡 单机容量问题,水平扩展表象:单机cpu使用率增高,memory占用增加,网络带宽使用增
2019-06-16
  目录
I I