zoukankan      html  css  js  c++  java
  • day72_淘淘商城项目_05_首页轮播图显示实现 + redis的安装及使用 + redis集群环境搭建 + redis实现缓存 + 缓存同步_匠心笔记

    课程计划:

    • 第五天
      • 1、首页轮播图的展示
      • 2、首页大广告展示流程图
      • 3、Redis的安装及使用
      • 4、Redis集群的搭建
      • 5、向业务逻辑中添加缓存
      • 6、Jedis的使用(redis的客户端)
      • 7、缓存同步
      • 8、Solr服务器安装

    1、首页轮播图的展示

      taotao-portal-web工程中,动态展示内容信息。
      前端团队:负责JS,html等开发。
      后端团队:负责后台的开发并提供数据给前端。

    1.1、功能分析

    只需要动态生成一个json数据,轮播图就可以动态展示。
    taotao-portal-web工程下的index.jsp中:
    Json数据格式:

    [
        {
            "srcB""http://image.taotao.com/images/2015/03/03/2015030304360302109345.jpg",
            "height"240,
            "alt""",
            "width"670,
            "src""http://image.taotao.com/images/2015/03/03/2015030304360302109345.jpg",
            "widthB"550,
            "href""http://sale.jd.com/act/e0FMkuDhJz35CNt.html?cpdad=1DLSUE",
            "heightB"240
        },
        {
            "srcB""http://image.taotao.com/images/2015/03/03/2015030304353109508500.jpg",
            "height"240,
            "alt""",
            "width"670,
            "src""http://image.taotao.com/images/2015/03/03/2015030304353109508500.jpg",
            "widthB"550,
            "href""http://sale.jd.com/act/UMJaAPD2VIXkZn.html?cpdad=1DLSUE",
            "heightB"240
        },
        ......
    ]

    分析:
      taotao-portal-web 需要自己自定义的POJO的类型数据的列表。
      taotao-content 是服务层公用的,可以被其他的系统(表现层的系统)来调用。
      为了通用性:taotao-content 服务层中获取tb_content的内容列表 即:list<TbContent>
      taotao-portal-web 表现层需要拿到tb_content的列表,然后进行转换成自定义的类型的数据列表即可。
      从tb_content表中取数据,根据(叶子节点)内容分类id查询列表(内容列表)。
      内容分类id固定,需要配置在属性文件中
      图片的width、height配置在属性文件中
      alt属性从字段sub_title中获取。
      src --> pic
      srcB --> pic2
      href --> url


    URL:/index
    参数:无。
    返回值:首页页面(数据是JSON,设置在Model中)

    业务逻辑:
      1、根据分类的id 查询内容列表(List<TbContent>)
      2、服务层发布服务。
      3、表现层引入服务。
      4、调用服务,转换成自定义的数据类型(Ad1Node)的列表。
      5、将数据列表设置到Model中,返回给页面。

    需要创建一个pojo转换成页面需要的json数据格式。放入taotao-portal-web工程的com.taotao.portal.pojo中。
    AD1Node.java

    /**
     * 前台大广告位需要的pojo数据类型
     * @author chenmingjun
     * @date 2018年11月18日下午4:03:34
     * @version 1.0
     */

    public class AD1Node implements Serializable {

        private static final long serialVersionUID = 1L;

        private String srcB;
        private Integer height;
        private String alt;
        private Integer width;
        private String src;
        private Integer widthB;
        private String href;
        private Integer heightB;

        public String getSrcB() {
            return srcB;
        }

        public void setSrcB(String srcB) {
            this.srcB = srcB;
        }

        public Integer getHeight() {
            return height;
        }

        public void setHeight(Integer height) {
            this.height = height;
        }

        public String getAlt() {
            return alt;
        }

        public void setAlt(String alt) {
            this.alt = alt;
        }

        public Integer getWidth() {
            return width;
        }

        public void setWidth(Integer width) {
            this.width = width;
        }

        public String getSrc() {
            return src;
        }

        public void setSrc(String src) {
            this.src = src;
        }

        public Integer getWidthB() {
            return widthB;
        }

        public void setWidthB(Integer widthB) {
            this.widthB = widthB;
        }

        public String getHref() {
            return href;
        }

        public void setHref(String href) {
            this.href = href;
        }

        public Integer getHeightB() {
            return heightB;
        }

        public void setHeightB(Integer heightB) {
            this.heightB = heightB;
        }

        @Override
        public String toString() {
            return "AD1Node [srcB=" + srcB + ", height=" + height + ", alt=" + alt + ", width=" + width + ", src=" + src
                    + ", widthB=" + widthB + ", href=" + href + ", heightB=" + heightB + "]";
        }
    }

    1.2、Dao层

      从tb_content表中取出数据,根据内容分类id查询内容列表。可以使用逆向工程生成的Mapper。

    1.3、Service层

    参数:Long categoryId
    返回值:List<TbContent>

        @Override
        public List<TbContent> getContentListByCategoryId(Long categoryId) {
            TbContentExample contentExample = new TbContentExample();
            Criteria criteria = contentExample.createCriteria();
            criteria.andCategoryIdEqualTo(categoryId);
            List<TbContent> list = contentMapper.selectByExample(contentExample);
            return list;
        }

    1.3.1、发布服务

      在taotao-content-service工程applicationContext-service.xml中发布服务

    1.4、表现层

      在taotao-portal-web中实现,查询首页轮播图的内容。

    1.4.1、引用服务

    先在taotao-protal-web项目中引入对taotao-content-interface的依赖,如图:


    在taotao-content-web工程springmvc.xml中引入服务

    1.4.2、Controller

    在首页展示之前,对数据进行处理,然后展示首页,需要在IndexController中实现。

    /**
     * 前台首页展示Controller
     * @author chenmingjun
     * @date 2018年12月31日
     * @version V1.0
     */

    @Controller
    public class IndexController {
        @Autowired
        private ContentService contentService;

        @Value("${AD1_CATEGORY_ID}")
        private Long AD1_CATEGORY_ID;
        @Value("${AD1_HEIGHT}")
        private Integer AD1_HEIGHT;
        @Value("${AD1_HEIGHT_B}")
        private Integer AD1_HEIGHT_B;
        @Value("${AD1_WIDTH}")
        private Integer AD1_WIDTH;
        @Value("${AD1_WIDTH_B}")
        private Integer AD1_WIDTH_B;

        /**
         * 展示前台首页,并展示首页轮播图(大广告)+展示小广告......
         * @return
         */

        @RequestMapping("/index")
        public String showIndex(Model model) {
            // 展示首页轮播图
            // 1、引入服务
            // 2、注入服务
            // 3、调用方法查询TbContent的列表
            List<TbContent> contentList = contentService.getContentListByCategoryId(AD1_CATEGORY_ID);
            // 4、把TbContent的列表转换为AD1Node列表
            List<AD1Node> aD1NodeList = new ArrayList<>();
            for (TbContent tbContent : contentList) {
                AD1Node aD1Node = new AD1Node();
                aD1Node.setAlt(tbContent.getSubTitle());
                aD1Node.setHref(tbContent.getUrl());
                aD1Node.setSrc(tbContent.getPic());
                aD1Node.setSrcB(tbContent.getPic2());
                aD1Node.setHeight(AD1_HEIGHT);
                aD1Node.setHeightB(AD1_HEIGHT_B);
                aD1Node.setWidth(AD1_WIDTH);
                aD1Node.setWidthB(AD1_WIDTH_B);
                aD1NodeList.add(aD1Node);
            }
            // 5、把AD1Node列表转换为JSON数据
            String json = JsonUtils.objectToJson(aD1NodeList);
            // 6、把JSON数据设置到request域中去(Model)
            model.addAttribute("ad1", json);
            // 展示小广告......等等省略了,以后实现
            return "index";
        }
    }

    属性文件所在位置:


    在taotao-portal-web中的springmvc.xml中还需要配置:
        <!-- 配置加载属性文件 -->
        <context:property-placeholder location="classpath:resource/resource.properties"/>

    1.5、测试

    后台数据如下图所示:


    前台浏览器显示界面:

    其他广告位同理。

    2、首页大广告展示流程图

      首页是系统的门户,也就是系统的入口。所以首页的访问量是这个系统最大的。如果每次展示首页都从数据库中查询首页的内容信息,那么势必会对数据库造成很大的压力,所以需要使用缓存来减轻数据库压力。
      实现缓存的工具有很多,现在比较流行的是redis。
      首页大广告展示流程:
      展示流程:先从缓存取,如果不存在,从数据库取出来,写入缓存,再返回页面;如果存在key,直接从缓存中取出来,展示到页面。
      同步缓存:当事务提交(更新,删除,插入)后,需要同步缓存,直接根据Key删除redis的key(清空缓存),再展示时,由上边的流程展示。

    3、Redis的安装及使用

    3.1、redis的下载

      官网地址:http://redis.io/
      下载地址:http://download.redis.io/releases/redis-3.0.0.tar.gz

    3.2、redis的安装

      安装redis需要c语言的编译环境,如果没有gcc需要在线安装。如下命令:

    [root@itheima ~]# yum -y install gcc-c++

      检测是否有gcc环境,只需输入命令:

    [root@itheima ~]# gcc
    [root@itheima ~]# gcc
    gcc: 致命错误:没有输入文件
    编译中断。
    [root@itheima ~]

    出现如上所述内容,表示gcc安装成功。
    安装步骤:
      第一步:将redis的源码包上传到linux系统。
      第二步:解压缩redis的源码包。
      第三步:进行编译。cd到解压后的目录,输入命令:make
      第四步:进行安装。输入命令:make install PREFIX=/usr/local/redis
      第五步:检查目录是否存在。在/usr/local/redis下有bin目录,则说明安装成功。

    3.3、连接redis

    3.3.1、redis的启动

    前端启动:在redis的安装目录下直接启动redis-server

    [root@itheima bin]# pwd
    /usr/local/redis/bin
    [root@itheima bin]# ./redis-server

    后台启动:
    把/root/redis-3.0.0/redis.conf复制到/usr/local/redis/bin目录下

    [root@itheima redis-3.0.0]# cp redis.conf /usr/local/redis/bin/

    修改配置文件:将daemonize由no改为yes
    再次启动redis:

    [root@itheima bin]# ./redis-server redis.conf 

    查看redis进程:

    [root@itheima bin]# ps aux | grep redis
    root       5845  0.4  0.2 140888  2104 ?        Ssl  22:51   0:00 ./redis-server *:6379
    root       5858  0.0  0.0 112720   980 pts/1    R+   22:51   0:00 grep --color=auto redis
    [root@itheima bin]

    前端启动,不能更换终端,影响下一步操作。而后台启动,只在进程中悄悄启动。推荐使用后台启动。

    3.3.2、使用redis自带的客户端redis-cli连接redis

    使用redis-cli建立连接:

    [root@itheima bin]# ./redis-cli 
    127.0.0.1:6379> ping
    PONG
    127.0.0.1:6379> 

    默认连接localhost运行在6379端口的redis服务。
    连接其他ip和端口:

    [root@itheima bin]# ./redis-cli -h 192.168.25.153 -p 6379
    192.168.25.153:6379> ping
    PONG
    192.168.25.153:6379> 

    -h:连接的服务器的地址
    -p:服务的端口号
    退出redis连接的三种方式
    第一种方式

    [root@itheima bin]# ./redis-cli 
    127.0.0.1:6379> quit
    [root@itheima bin]

    第二种方式

    [root@itheima bin]# ./redis-cli 
    127.0.0.1:6379> exit
    [root@itheima bin]

    第三种方式

    Ctrl+C

    3.3.3、使用redis的桌面客户端软件连接redis

    先安装:redis-desktop-manager-0.7.9.809.exe,注意:该软件只能连接redis单机版。


    安装好后,双击打开软件

    redis默认有16个库,如果不指定库,默认存储在索引为0的库中。

    3.3.4、关闭redis服务

    第一种:通过使用redis自带的客户端redis-cli进行关闭,使用 shutdown 命令。

    [root@itheima bin]# ./redis-cli 
    127.0.0.1:6379> shutdown
    not connected> 

    第二种:使用 kill 命令。
    找到对应的redis的进程id 然后使用命令:(pid为进程id)

    [root@itheima bin]# kill -9 pid

    3.4、redis五种数据类型

    3.4.1、String:key-value

    redis命令不区分大小写,但是key区分大小写的。
    redis中所有的数据都是字符串
    redis是单线程的,所以不适合存储比较大的数据,最好要存小且能快速处理完的东西。
    使用incr命令,如果key不存在,会自动创建key并自动+1。
      set key value   设置值
      get key   获取值
      incr key   加一
      decr key   减一

    127.0.0.1:6379set key1 2
    OK
    127.0.0.1:6379set key2 value2
    OK
    127.0.0.1:6379get key1
    "2"
    127.0.0.1:6379get key2
    "value2"
    127.0.0.1:6379Get key2
    "value2"
    127.0.0.1:6379incr key1
    (integer) 3
    127.0.0.1:6379decr key1
    (integer) 2
    127.0.0.1:6379

    3.4.2、Hash: key-field-value

    相当于一个key对应一个map (map中又是key-value),一般应用于归类。
      hset key field value
      hget key field
      hincrby key field num

    127.0.0.1:6379hset hkey1 filed1 value1
    (integer) 1
    127.0.0.1:6379hset hkey1 filed2 2
    (integer) 1
    127.0.0.1:6379hget hkey1 filed1
    "value1"
    127.0.0.1:6379hget hkey1 filed2
    "2"
    127.0.0.1:6379hincrby hkey1 filed2 1
    (integer) 3
    127.0.0.1:6379hincrby hkey1 filed2 -1
    (integer) 2
    127.0.0.1:6379

    3.4.3、List

    List是有顺序可重复(数据结构中的:双链表,队列)
    可作为链表,可以从左添加元素,也可以从右添加元素。
      lpush list a b c d   (从左添加元素)
      rpush list 1 2 3 4   (从右边添加元素)
      lrange list 0 -1   (从 0 到 -1 元素查看:也就表示查看所有)
      lpop list   (从左边取,删除)
      rpop list   (从右边取,删除)

    127.0.0.1:6379> lpush list1 a b c d
    (integer) 4
    127.0.0.1:6379> lrange list1 0 -1
    1"d"
    2"c"
    3"b"
    4"a"
    127.0.0.1:6379> rpush list1 1 2 3 4
    (integer) 8
    127.0.0.1:6379> lrange list1 0 -1
    1"d"
    2"c"
    3"b"
    4"a"
    5"1"
    6"2"
    7"3"
    8"4"
    127.0.0.1:6379> lpop list1
    "d"
    127.0.0.1:6379> lrange list1 0 -1
    1"c"
    2"b"
    3"a"
    4"1"
    5"2"
    6"3"
    7"4"
    127.0.0.1:6379> rpop list1
    "4"
    127.0.0.1:6379> lrange list1 0 -1
    1"c"
    2"b"
    3"a"
    4"1"
    5"2"
    6"3"
    127.0.0.1:6379> 

    3.4.4、Set

    Set无顺序,不能重复。
      sadd set1 a b c d d   (向set1中添加元素)元素不重复
      smembers set1   (查询元素)
      srem set1 a   (删除元素)

    127.0.0.1:6379sadd set1 a a a b b c
    (integer) 3
    127.0.0.1:6379smembers set1
    1) "c"
    2) "b"
    3) "a"
    127.0.0.1:6379srem set1 a
    (integer) 1
    127.0.0.1:6379smembers set1
    1) "c"
    2) "b"
    127.0.0.1:6379

    3.4.5、SortedSet(zset)

    SortedSet有顺序,不能重复。
    适合做排行榜 排序需要一个分数属性。
      zadd zset1 8 a 4 b 5 c 1 d   (添加元素:zadd key score member)
      zrange key start stop [WITHSCORES]   (查看所有元素:zrange key 0 -1 withscores)
        如果要查看分数,需要加上withscores
      zrange zset1 0 -1   (从小到大)
      zrevrange zset1 0 -1   (从大到小)
      zincrby zset1 score member   (对元素 member 增加 score)

    127.0.0.1:6379> zadd zset1 8 a 4 b 5 c 1 d
    (integer) 4
    127.0.0.1:6379> zrange zset1 0 -1
    1"d"
    2"b"
    3"c"
    4"a"
    127.0.0.1:6379> zadd zset1 9 a
    (integer) 0
    127.0.0.1:6379> zrange zset1 0 -1
    1"d"
    2"b"
    3"c"
    4"a"
    127.0.0.1:6379> zrange zset1 0 -1 withscores
    1"d"
    2"1"
    3"b"
    4"4"
    5"c"
    6"5"
    7"a"
    8"9"
    127.0.0.1:6379> zrevrange zset1 0 -1
    1"a"
    2"c"
    3"b"
    4"d"
    127.0.0.1:6379> zrevrange zset1 0 -1 withscores
    1"a"
    2"9"
    3"c"
    4"5"
    5"b"
    6"4"
    7"d"
    8"1"
    127.0.0.1:6379> 

    3.5、key 命令

    expire key second   设置key的过期时间
    ttl key             查看剩余时间(-2 表示不存在,-1 表示已被持久化(永不过期),正数表示剩余的时间)
    persist key         清除过期时间,也即持久化,持久化成功体提示 1,不成功提示 0
    del key             删除key  
    exists key          若key存在,返回1,否则返回0
    select 0            表示选择0号数据库。默认是0号数据库。

    redis 默认有16个库 select num 从0开始换库

    keys *              获取redis里面所有的key
    dbsize              查看当前数据库的key的数量
    flushdb             清空当前库的所有key
    flushall            清空所有库的key
    exists key          是否存在该key
    move key db         把当前库的key移到db号库中
    type key            查看key的类型

    3.6、redis持久化方案

    redis 数据都放在内存中。如果机器挂掉,内存的数据就不存在。
    需要做持久化处理,将内存中的数据保存在磁盘,下一次启动的时候就可以恢复数据到内存中。
      方案1:RDB 快照形式(定期将当前时刻的数据保存磁盘中)会产生一个dump.rdb文件。
        特点:会存在数据丢失,性能较好,数据备份。
      方案2:AOF:append only file (所有对redis的操作命令记录在aof文件中),恢复数据,重新执行一遍即可。
        特点:每秒保存(也可能会存在数据丢失),数据比较完整,耗费性能。其实也可以设置成每次保存,但是此就失去了缓存的意义了。
    redis默认开启RDB。如下图:


    redis.conf 中默认设置了保存规则及时间间隔。
    save 开头的一行就是持久化配置,可以配置多个条件(每行配置一个条件),每个条件之间是“或”的关系。
        save 900 1      表示15分钟(900秒)内至少1个键被更改则进行快照。
        save 300 10     表示5分钟(300秒)内至少10个键被更改则进行快照。
        save 60 10000   表示1分钟(60秒)内至少10000个键被更改则进行快照。

    AOF开启设置,修改 redis.conf 文件,如下图:
    将appendonly设置为yes即可。


    同时开启两个持久化方案,则按照AOF的持久化放案恢复数据
    默认是按照RDB的方式恢复数据,如果开启了AOF,就是用AOF恢复数据,数据是存在于/usr/local/redis/bin/appendonly.aof文件中。

    4、Redis集群的搭建

    4.1、redis-cluster架构图


    架构细节详解如下:
    架构细节:
        (1) 所有的 redis 节点彼此互联(`PING-PONG机制`),内部使用`二进制协议`优化传输速度和带宽。
        (2) 节点的 fail(失败) 是通过集群中`超过半数的节点检测失效`时才生效。即`集群搭建中主机的个数为奇数`
        (3) 客户端与 redis 节点直连,不需要中间 proxy层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
        (4) redis-cluster 把所有的物理节点映射到 [0-16383]slot(槽) 上,cluster 负责维护 node<-->slot<-->value

    Redis 集群中内置了 `16384 个哈希槽`,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果`对 16384 求余数`,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量`大致均等`的将哈希槽映射到不同的节点。如下图所示:

    哈希槽的图解:


    redis-cluster投票,容错机制图解:

    4.2、redis集群的搭建

      因为有投票容错机制,所以需要至少3个节点(奇数),为了集群的高可用,为每一个节点增加一个备份机(共6台服务器)。
      搭建伪分布式集群方案:在一台机器里面运行6个redis实例。端口需要不同即可(7001-7006)。

    4.2.1、集群搭建环境

    4.2.2、搭建步骤

    参考文章链接:https://www.cnblogs.com/chenmingjun/p/9903889.html
    创建集群命令:

    [root@itheima redis-cluster]# ./redis-trib.rb create --replicas 1 192.168.25.153:7001 192.168.25.153:7002 192.168.25.153:7003 192.168.25.153:7004 192.168.25.153:7005 192.168.25.153:7006

    4.3、集群的使用方法

    redis-cli连接集群。连接主节点或者从节点都可以。

    [root@itheima redis-cluster]# 7001/redis-cli -h 192.168.25.153 -p 7006 -c
    192.168.25.153:7006> set key1 123
    -> Redirected to slot [9189] located at 192.168.25.153:7002
    OK
    192.168.25.153:7002> keys *
    1"key1"
    192.168.25.153:7002> set key2 abc
    -> Redirected to slot [4998] located at 192.168.25.153:7001
    OK
    192.168.25.153:7001> keys *
    1"key2"
    192.168.25.153:7001> set key3 bbbb
    OK
    192.168.25.153:7001> set key4 cccc
    -> Redirected to slot [13120] located at 192.168.25.153:7003
    OK
    192.168.25.153:7003> 

    -c:代表连接的是redis集群
    使用命令操作redis集群是和单机版的redids是一样的。

    192.168.25.153:7003cluster info   #查询集群的信息
    cluster_state:ok
    cluster_slots_assigned:16384
    cluster_slots_ok:16384
    cluster_slots_pfail:0
    cluster_slots_fail:0
    cluster_known_nodes:6
    cluster_size:3
    cluster_current_epoch:6
    cluster_my_epoch:3
    cluster_stats_messages_sent:1623
    cluster_stats_messages_received:1623
    192.168.25.153:7003cluster nodes  #查询集群的节点
    ecb5aafd409740004ac3bf298318e2243f562e4e 192.168.25.153:7004 slave b207611daffa174990c64499495fc42ce3978767 0 1542619629901 4 connected
    30775ef3b509604b604b76a4407334fe1fb6304f 192.168.25.153:7006 slave c6514cdac6977ef8a8fbee8c7d5c3e4edd7df585 0 1542619634936 6 connected
    fd369b55daa7cdd8640e1e1bfa8fa2ab9cfe39cd 192.168.25.153:7002 master - 0 1542619633928 2 connected 5461-10922
    c6514cdac6977ef8a8fbee8c7d5c3e4edd7df585 192.168.25.153:7003 myself,master - 0 0 3 connected 10923-16383
    75a701f4cd545ee704ded294c17bbfa3f02eaee1 192.168.25.153:7005 slave fd369b55daa7cdd8640e1e1bfa8fa2ab9cfe39cd 0 1542619631916 5 connected
    b207611daffa174990c64499495fc42ce3978767 192.168.25.153:7001 master - 0 1542619632921 1 connected 0-5460
    192.168.25.153:7003

    5、Java客户端Jedis的使用方法

    需要把jedis依赖的jar包添加到工程中。Maven工程中需要把jedis的坐标添加到依赖。
    推荐添加到服务层。本例中是:taotao-content-service工程中。
    第一步:添加jar包的依赖


    在taotao-content-service中添加测试方法,测试如下:

    5.1、通过单实例连接redis服务器单机版

        @Test
        public void jedisClientTest() throws Exception 
    {
            // 1、创建一个Jedis的连接对象(单实例),需要指定服务端的ip及端口
            Jedis jedis = new Jedis("192.168.25.153"6379);
            // 2、选择要操作的数据库
            jedis.select(2);
            // 3、执行redis命令,即使用Jedis对象操作数据库,每个redis命令对应一个方法
            jedis.set("s4""晓艺,你还好吗?");
            // 从redis中取值
            String result = jedis.get("s4");
            // 打印结果
            System.out.println(result);
            // 4、关闭连接
            jedis.close();
        }

    通过单实例连接redis服务器,我们发现每一次连接都需要创建连接,创建连接是比较浪费资源的,所以我们需要使用连接池来连接redis数据库。

    5.2、通过连接池连接redis服务器单机版

    测试代码如下:

        /**
         * 通过连接池连接redis服务器单机版
         */

        @Test
        public void jedisPoolTest() throws Exception 
    {
            // 1、创建一个连接池JedisPool对象(单实例),需要指定服务端的ip及端口
            JedisPool jedisPool = new JedisPool("192.168.25.153"6379);
            // 2、从连接池JedisPool中获取jedis会话对象
            Jedis jedis = jedisPool.getResource();
            // 3、使用redis命令操作redis数据库(方法级别使用:用完之后就要关闭)
            String result = jedis.get("s11");
            System.out.println(result);
            // 4、关闭连接,操作完毕后关闭jedis对象,连接池要回收资源。
            jedis.close();
            // 5、关闭连接池对象JedisPool
            jedisPool.close();
        }

    5.3、使用spring整合JedisPool连接redis服务器单机版

    添加spring的jar包
    配置spring配置文件applicationContext.xml
    applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
            http://www.springframework.org/schema/mvc 
            http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-3.2.xsd 
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "
    >


        <!-- 连接池配置 -->
        <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
            <!-- 最大连接数 -->
            <property name="maxTotal" value="30" />
            <!-- 最大空闲连接数 -->
            <property name="maxIdle" value="10" />
            <!-- 每次释放连接的最大数目 -->
            <property name="numTestsPerEvictionRun" value="1024" />
            <!-- 释放连接的扫描间隔(毫秒) -->
            <property name="timeBetweenEvictionRunsMillis" value="30000" />
            <!-- 连接最小空闲时间 -->
            <property name="minEvictableIdleTimeMillis" value="1800000" />
            <!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
            <property name="softMinEvictableIdleTimeMillis" value="10000" />
            <!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
            <property name="maxWaitMillis" value="1500" />
            <!-- 在获取连接的时候检查有效性, 默认false -->
            <property name="testOnBorrow" value="false" />
            <!-- 在空闲时检查有效性, 默认false -->
            <property name="testWhileIdle" value="true" />
            <!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
            <property name="blockWhenExhausted" value="false" />
        </bean>

        <!-- redis单机 通过连接池连接redis服务器 -->
        <bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="close">
            <constructor-arg name="poolConfig" ref="jedisPoolConfig" />
            <constructor-arg name="host" value="192.168.5.128" />
            <constructor-arg name="port" value="6379" />
        </bean>
    </beans>

    测试代码如下:

        /**
         * 通过连接池连接redis服务器单机版(整合spring)
         */

        @Test
        public void jedisPoolTest() 
    {
            JedisPool pool = (JedisPool) applicationContext.getBean("jedisPool");
            Jedis jedis = null;
            try {
                jedis = pool.getResource();
                jedis.set("s5""lisi");
                String name = jedis.get("s5");
                System.out.println(name);
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                if (jedis != null) {
                    // 关闭连接
                    jedis.close();
                }
            }
        }

    5.4、使用JedisCluster类连接redis服务器集群版

    第一步:使用JedisCluster对象。需要使用Set<HostAndPort>参数。表示redis节点的列表。
    第二步:直接使用JedisCluster对象操作redis。该对象在系统中是单例存在的。
    第三步:打印结果。
    第四步:在系统关闭前,关闭JedisCluster对象。

        /**
         * 使用JedisCluster类连接redis集群版
         */

        @Test
        public void jedisClusterTest() throws Exception 
    {
            // 需要使用 Set<HostAndPort> 参数。表示redis节点的列表。
            Set<HostAndPort> nodes = new HashSet<>();
            nodes.add(new HostAndPort("192.168.25.153"7001));
            nodes.add(new HostAndPort("192.168.25.153"7002));
            nodes.add(new HostAndPort("192.168.25.153"7003));
            nodes.add(new HostAndPort("192.168.25.153"7004));
            nodes.add(new HostAndPort("192.168.25.153"7005));
            nodes.add(new HostAndPort("192.168.25.153"7006));
            // 创建JedisCluster对象,该对象在系统中是单例存在的
            JedisCluster jedisCluster = new JedisCluster(nodes);
            // 执行JedisCluster对象中的方法,方法和redis中的一一对应
            jedisCluster.set("cluster-test""Jedis connects redis cluster test");
            String result = jedisCluster.get("cluster-test");
            System.out.println(result);
            // 程序结束时需要关闭JedisCluster对象
            jedisCluster.close();
        }

    5.5、使用spring整合JedisCluster连接redis服务器集群版

    配置applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
            http://www.springframework.org/schema/mvc 
            http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-3.2.xsd 
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "
    >

        <!-- 连接池配置 -->
        <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
            <!-- 最大连接数 -->
            <property name="maxTotal" value="30" />
            <!-- 最大空闲连接数 -->
            <property name="maxIdle" value="10" />
            <!-- 每次释放连接的最大数目 -->
            <property name="numTestsPerEvictionRun" value="1024" />
            <!-- 释放连接的扫描间隔(毫秒) -->
            <property name="timeBetweenEvictionRunsMillis" value="30000" />
            <!-- 连接最小空闲时间 -->
            <property name="minEvictableIdleTimeMillis" value="1800000" />
            <!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
            <property name="softMinEvictableIdleTimeMillis" value="10000" />
            <!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
            <property name="maxWaitMillis" value="1500" />
            <!-- 在获取连接的时候检查有效性, 默认false -->
            <property name="testOnBorrow" value="true" />
            <!-- 在空闲时检查有效性, 默认false -->
            <property name="testWhileIdle" value="true" />
            <!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
            <property name="blockWhenExhausted" value="false" />
        </bean>
        <!-- redis集群 -->
        <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
            <constructor-arg index="0">
                <set>
                    <bean class="redis.clients.jedis.HostAndPort">
                        <constructor-arg index="0" value="192.168.5.128"></constructor-arg>
                        <constructor-arg index="1" value="7001"></constructor-arg>
                    </bean>
                    <bean class="redis.clients.jedis.HostAndPort">
                        <constructor-arg index="0" value="192.168.5.128"></constructor-arg>
                        <constructor-arg index="1" value="7002"></constructor-arg>
                    </bean>
                    <bean class="redis.clients.jedis.HostAndPort">
                        <constructor-arg index="0" value="192.168.5.128"></constructor-arg>
                        <constructor-arg index="1" value="7003"></constructor-arg>
                    </bean>
                    <bean class="redis.clients.jedis.HostAndPort">
                        <constructor-arg index="0" value="192.168.5.128"></constructor-arg>
                        <constructor-arg index="1" value="7004"></constructor-arg>
                    </bean>
                    <bean class="redis.clients.jedis.HostAndPort">
                        <constructor-arg index="0" value="192.168.5.128"></constructor-arg>
                        <constructor-arg index="1" value="7005"></constructor-arg>
                    </bean>
                    <bean class="redis.clients.jedis.HostAndPort">
                        <constructor-arg index="0" value="192.168.5.128"></constructor-arg>
                        <constructor-arg index="1" value="7006"></constructor-arg>
                    </bean>
                </set>
            </constructor-arg>
            <constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg>
        </bean>
    </beans>

    测试代码:

        private ApplicationContext applicationContext;
        @Before
        public void init() 
    {
            applicationContext = new ClassPathXmlApplicationContext(
                    "classpath:applicationContext.xml");
        }

        /**
        * 使用spring实现Jedis连接Redis集群
       */

        @Test
        public void testJedisCluster2() 
    {
            JedisCluster jedisCluster = (JedisCluster) applicationContext.getBean("jedisCluster");

            jedisCluster.set("name""xiaoyi");
            String value = jedisCluster.get("name");
            System.out.println(value);
        }

    6、向业务逻辑中添加缓存

      因为集群是比较消耗成本的,所以在公司中,一般生产环境使用集群开发环境使用单机版
      我们在项目整合中都需要有。使用设计模式之策略模式:接口+实现类。
      可以开发一个接口,有单机版的实现类集群版的实现类。使用时可以面向接口开发,不影响业务逻辑,使用spring管理实现类,部署时切换实现类即可。

    6.1、接口封装

      常用的操作redis的方法提取出一个接口,分别对应单机版和集群版创建两个实现类。

    6.1.1、接口定义

    package com.taotao.jedis;

    import java.util.Map;

    public interface JedisClient {

        String set(String key, String value);

        String get(String key);

        Boolean exists(String key);

        Long expire(String key, int seconds);

        Long ttl(String key);

        Long incr(String key);

        Long hset(String key, String field, String value);

        String hget(String key, String field);

        Long hdel(String key, String... field); // 删除hkey

        Map<StringString> hgetAll(String key); // 查询所有的hash类型的列表
    }

    6.1.2、redis单机版实现类

    package com.taotao.jedis;

    import java.util.Map;

    import org.springframework.beans.factory.annotation.Autowired;

    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;

    public class JedisClientPool implements JedisClient {

        @Autowired
        private JedisPool jedisPool;

        @Override
        public String set(String key, String value) {
            Jedis jedis = jedisPool.getResource();
            String result = jedis.set(key, value);
            jedis.close();
            return result;
        }

        @Override
        public String get(String key) {
            Jedis jedis = jedisPool.getResource();
            String result = jedis.get(key);
            jedis.close();
            return result;
        }

        @Override
        public Boolean exists(String key) {
            Jedis jedis = jedisPool.getResource();
            Boolean result = jedis.exists(key);
            jedis.close();
            return result;
        }

        @Override
        public Long expire(String key, int seconds) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.expire(key, seconds);
            jedis.close();
            return result;
        }

        @Override
        public Long ttl(String key) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.ttl(key);
            jedis.close();
            return result;
        }

        @Override
        public Long incr(String key) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.incr(key);
            jedis.close();
            return result;
        }

        @Override
        public Long hset(String key, String field, String value) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.hset(key, field, value);
            jedis.close();
            return result;
        }

        @Override
        public String hget(String key, String field) {
            Jedis jedis = jedisPool.getResource();
            String result = jedis.hget(key, field);
            jedis.close();
            return result;
        }

        @Override
        public Long hdel(String key, String... field) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.hdel(key, field);
            jedis.close();
            return result;
        }

        @Override
        public Map<String, String> hgetAll(String key) {
            Jedis jedis = jedisPool.getResource();
            Map<String, String> map = jedis.hgetAll(key);
            jedis.close();
            return map;
        }
    }

    applicationContext-redis.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context" 
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:aop="http://www.springframework.org/schema/aop" 
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
        http://code.alibabatech.com/schema/dubbo 
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd
        http://www.springframework.org/schema/util 
        http://www.springframework.org/schema/util/spring-util-4.2.xsd"
    >

        <!-- 注意:我们已经在 applicationContext-service.xml 配置了包扫描器,该配置做了两件事:既开启注解,又扫描包 -->
        <!-- <context:annotation-config></context:annotation-config> 表示开启注解,但是不能扫描包。所以这句配置可以不用了。-->
        <!-- 如果使用方式二,我们在加载配置文件的时候,要加载所有的配置文件才可以。(本案例中使用的是方式一) -->
        <!-- 如果使用方式一,我们在加载配置文件的时候,要加载单个的配置文件即可。 -->

        <!-- 配置对redis单机版的连接 -->
        <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
            <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
            <constructor-arg name="port" value="6379"></constructor-arg>
        </bean>
        <!-- 手动配置的jedis单机版客户端实现类bean:会在spring容器中加载 ,原因是:我们已经在 applicationContext-service.xml 配置了包扫描器,该配置做了两件事:既开启注解,又扫描包-->
        <bean id="jedisClientPool" class="com.taotao.jedis.手动配置的bean"/> 
    </beans>

    注意:在spring中要使用注解,我们先要开启注解,开启注解有两种方式
      方式一:配置<context:annotation-config></context:annotation-config>注解,表示开启注解,但是不能扫描包。
      方式二:配置包扫描器<context:component-scan base-package="com.taotao.content.service"></context:component-scan>注解,表示扫描所有需要带@Service注解的类,该配置做了两件事:既开启注解,又扫描包。
    所以我们只要配置了包扫描器,那么我们手动配置的bean就会在spring容器中加载。
    特别注意:
      如果使用方式二,我们在加载配置文件的时候,要加载所有的配置文件才可以。(本案例中使用的是方式一)
      如果使用方式一,我们在加载配置文件的时候,要加载单个的配置文件即可。

    6.1.3、redis集群版实现类

    package com.taotao.jedis;

    import java.util.Map;

    import org.springframework.beans.factory.annotation.Autowired;

    import redis.clients.jedis.JedisCluster;

    public class JedisClientCluster implements JedisClient {

        @Autowired
        private JedisCluster jedisCluster;

        @Override
        public String set(String key, String value) {
            return jedisCluster.set(key, value);
        }

        @Override
        public String get(String key) {
            return jedisCluster.get(key);
        }

        @Override
        public Boolean exists(String key) {
            return jedisCluster.exists(key);
        }

        @Override
        public Long expire(String key, int seconds) {
            return jedisCluster.expire(key, seconds);
        }

        @Override
        public Long ttl(String key) {
            return jedisCluster.ttl(key);
        }

        @Override
        public Long incr(String key) {
            return jedisCluster.incr(key);
        }

        @Override
        public Long hset(String key, String field, String value) {
            return jedisCluster.hset(key, field, value);
        }

        @Override
        public String hget(String key, String field) {
            return jedisCluster.hget(key, field);
        }

        @Override
        public Long hdel(String key, String... field) {
            return jedisCluster.hdel(key, field);
        }

        @Override
        public Map<String, String> hgetAll(String key) {
            return jedisCluster.hgetAll(key);
        }
    }

    添加此配置到applicationContext-redis.xml中

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context" 
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:aop="http://www.springframework.org/schema/aop" 
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
        http://code.alibabatech.com/schema/dubbo 
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd
        http://www.springframework.org/schema/util 
        http://www.springframework.org/schema/util/spring-util-4.2.xsd"
    >

        <!-- 注意:我们已经在 applicationContext-service.xml 配置了包扫描器,该配置做了两件事:既开启注解,又扫描包 -->
        <!-- <context:annotation-config></context:annotation-config> 表示开启注解,但是不能扫描包。所以这句配置可以不用了。-->
        <!-- 如果使用方式二,我们在加载配置文件的时候,要加载所有的配置文件才可以。(本案例中使用的是方式一) -->
        <!-- 如果使用方式一,我们在加载配置文件的时候,要加载单个的配置文件即可。 -->

        <!-- 配置对redis单机版的连接 -->
        <!-- 单机版和集群版不能共存,使用单机版时注释集群版的配置。使用集群版,把单机版注释。   
        <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
            <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
            <constructor-arg name="port" value="6379"></constructor-arg>
        </bean>
            手动配置的jedis单机版客户端实现类bean:会在spring容器中加载 
        <bean id="jedisClientPool" class="com.taotao.jedis.JedisClientPool"/>    
        -->

        <!-- 配置对redis集群版的连接 -->
        <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
            <constructor-arg>
                <set>
                    <bean class="redis.clients.jedis.HostAndPort">
                        <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
                        <constructor-arg name="port" value="7001"></constructor-arg>
                    </bean>
                    <bean class="redis.clients.jedis.HostAndPort">
                        <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
                        <constructor-arg name="port" value="7002"></constructor-arg>
                    </bean>
                    <bean class="redis.clients.jedis.HostAndPort">
                        <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
                        <constructor-arg name="port" value="7003"></constructor-arg>
                    </bean>
                    <bean class="redis.clients.jedis.HostAndPort">
                        <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
                        <constructor-arg name="port" value="7004"></constructor-arg>
                    </bean>
                    <bean class="redis.clients.jedis.HostAndPort">
                        <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
                        <constructor-arg name="port" value="7005"></constructor-arg>
                    </bean>
                    <bean class="redis.clients.jedis.HostAndPort">
                        <constructor-arg name="host" value="192.168.25.153"></constructor-arg>
                        <constructor-arg name="port" value="7006"></constructor-arg>
                    </bean>
                </set>
            </constructor-arg>
        </bean>
        <!-- 手动配置的jedis集群版客户端实现类bean:会在spring容器中加载 -->
        <bean id="jedisClientCluster" class="com.taotao.jedis.JedisClientCluster"/> 
    </beans>

    注意:单机版和集群版不能共存,使用单机版时注释集群版的配置。使用集群版,把单机版注释。

    6.2、封装代码测试

    策略设计模式的优势,只要切换实现类(即简单注释下spring配置文件),不需修改测试类。
    测试代码:

    public class JedisSpringTest {

        @Test
        public void testJedisClient() throws Exception 
    {
            // 初始化Spring容器,我们已经在 applicationContext-service.xml 配置了包扫描器,需要我们在加载配置文件的时候,要加载所有的配置文件才可以。
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml"); // 加载所有配置文件
            // 从容器中获得JedisClient对象
            JedisClient jedisClient = applicationContext.getBean(JedisClient.class);
            jedisClient.set("first""100");
            String result = jedisClient.get("first");
            System.out.println(result);             
        }
    }

    命令行中进行测试如下:没有问题

    [root@itheima bin]# ./redis-cli -p 7001 -c
    127.0.0.1:7001> get first
    -> Redirected to slot [11149] located at 192.168.25.153:7003
    "100"
    192.168.25.153:7003> 

    6.3、向业务逻辑中添加缓存

    由于我们处于开发阶段,所以我们先将我们的连接方式切换为单机版。

    6.3.1、功能分析

    查询内容列表时添加缓存。
      1、查询数据库之前先查询缓存。
      2、查询到结果,直接响应结果。
      3、查询不到,缓存中没有需要查询数据库。
      4、把查询结果添加到缓存中。
      5、返回结果。
    向redis中添加缓存,添加缓存我们使用什么样的数据类型呢?
      key:categoryId 字段(field),即我们的查询条件
      value:内容列表。由于redis中不能存java对象,所以需要把java对象转换成json格式的数据(即json字符串),可以使用工具类。
    推荐使用hash类型,因为hash类型可以对key进行归类。
    HASH_KEY:HASH
          |--KEY:VALUE
          |--KEY:VALUE
          |--KEY:VALUE
          |--KEY:VALUE
    注意:添加缓存不能影响正常业务逻辑。

    6.3.2、代码实现

    在ContentServiceImpl实现类中添加缓存。
    部分代码如下:

        // 通过spring容器注入JedisClient对象
        @Autowired
        private JedisClient jedisClient;

        @Value("${INDEX_CONTENT_KEY}")
        private String INDEX_CONTENT_KEY;

        @Override
        public List<TbContent> getContentListByCategoryId(Long categoryId) {
            // 查询数据库之前先查询缓存
            // 注意:添加了缓存不能影响正常业务逻辑,所以我们使用try...catch,将异常捕获,其余的向下继续执行
            try {
                // 从缓存中查询到结果,需要把json格式的数据转成List返回
                String json = jedisClient.hget(INDEX_CONTENT_KEY, categoryId + "");
                if (StringUtils.isNotBlank(json)) { // 如果json不是空才去转换
                    List<TbContent> list = JsonUtils.jsonToList(json, TbContent.class);
                    return list;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            // 第一次查询没有,缓存中没有命中,则去查询数据库
            TbContentExample contentExample = new TbContentExample();
            Criteria criteria = contentExample.createCriteria();
            criteria.andCategoryIdEqualTo(categoryId);
            List<TbContent> list = contentMapper.selectByExample(contentExample);

            // 把从数据库中查询到的结果添加到缓存
            // 注意:添加了缓存不能影响正常业务逻辑,所以我们使用try...catch,将异常捕获,其余的向下继续执行
            try {
                // 添加缓存我们使用什么样的数据类型呢?答:使用String或者Hash均可,推荐使用Hash类型,因为Hash类型可以对key进行归类。
                jedisClient.hset(INDEX_CONTENT_KEY, categoryId + "", JsonUtils.objectToJson(list));
            } catch (Exception e) {
                e.printStackTrace();
            }

            // 返回结果
            return list;
        }

    属性文件所在的位置:


    要想能加载还需要在taotao-content-service工程applicationContext-dao.xml中配置:
        <!-- 加载配置文件 -->
        <context:property-placeholder location="classpath:properties/*.properties" />

    6.4、缓存同步

    在对内容信息做增删改操作后只需要把对应缓存key删除即可。
    我们以【添加内容】为例,可以根据categoryId删除。

        @Override
        public TaotaoResult saveContent(TbContent content) {
            // 注入Mapper
            // 补全属性
            content.setCreated(new Date());
            content.setUpdated(content.getCreated());
            // 将内容数据保存至内容表
            contentMapper.insert(content);
            try {
                // 同步缓存:在对内容信息做【增删改】操作后只需要把对应缓存key删除即可。
                jedisClient.hdel(INDEX_CONTENT_KEY, content.getCategoryId().toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return TaotaoResult.ok();
        }

    【更新内容】和【删除内容】加入缓存同步代码如下:

        @Override
        public TaotaoResult updateContent(TbContent content) {
            // 设置创建时间
            content.setCreated(new Date());
            // 设置更新时间
            content.setUpdated(new Date());
            // 将内容数据更新至内容表(全字段更新)
            contentMapper.updateByPrimaryKeyWithBLOBs(content);
            try {
                // 同步缓存:在对内容信息做【增删改】操作后只需要把对应缓存key删除即可。
                jedisClient.hdel(INDEX_CONTENT_KEY, content.getCategoryId().toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return TaotaoResult.ok();
        }

        @Override
        public TaotaoResult getContentText(Long id) {
            TbContent content = contentMapper.selectByPrimaryKey(id);
            return TaotaoResult.ok(content);
        }

        @Override
        public TaotaoResult deleteContent(List<Long> ids) {
            for (Long id : ids) {
                TbContent content = contentMapper.selectByPrimaryKey(id);
                contentMapper.deleteByPrimaryKey(id);
                try {
                    // 同步缓存:在对内容信息做【增删改】操作后只需要把对应缓存key删除即可。
                    jedisClient.hdel(INDEX_CONTENT_KEY, content.getCategoryId().toString());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return TaotaoResult.ok();
        }

    7、附录

    我们在搭建redis集群的时候,会写一些自定义的批处理文件,如下图所示:


    具体内容如下:
    redis_start_all.sh

     

    cd redis01/
    ./redis-server redis.conf
    cd ..

    cd redis02/
    ./redis-server redis.conf
    cd ..

    cd redis03/
    ./redis-server redis.conf
    cd ..

    cd redis04/
    ./redis-server redis.conf
    cd ..

    cd redis05/
    ./redis-server redis.conf
    cd ..

    cd redis06/
    ./redis-server redis.conf
    cd ..

    redis_shutdown_all.sh

    cd redis01/
    ./redis-cli -h 192.168.25.153 -p 7001 shutdown
    cd ..

    cd redis02/
    ./redis-cli -h 192.168.25.153 -p 7002 shutdown
    cd ..

    cd redis03/
    ./redis-cli -h 192.168.25.153 -p 7003 shutdown
    cd ..

    cd redis04/
    ./redis-cli -h 192.168.25.153 -p 7004 shutdown
    cd ..

    cd redis05/
    ./redis-cli -h 192.168.25.153 -p 7005 shutdown
    cd ..

    cd redis06/
    ./redis-cli -h 192.168.25.153 -p 7006 shutdown
    cd ..

    redis_delete_aof_rdb_nodes.sh

    cd redis01/
    rm -rf appendonly.aof dump.rdb nodes.conf
    cd ..

    cd redis02/
    rm -rf appendonly.aof dump.rdb nodes.conf
    cd ..

    cd redis03/
    rm -rf appendonly.aof dump.rdb nodes.conf
    cd ..

    cd redis04/
    rm -rf appendonly.aof dump.rdb nodes.conf
    cd ..

    cd redis05/
    rm -rf appendonly.aof dump.rdb nodes.conf
    cd ..

    cd redis06/
    rm -rf appendonly.aof dump.rdb nodes.conf
    cd ..
  • 相关阅读:
    简单了解winform
    SqL语句基础之增删改查
    数据库之表
    数据库基本概念与操作
    搞死人不偿命的 Bank系统
    for的循环题
    .net framework 版本汇总
    LinqToEntity模糊查询的方法选择
    日常工作中的点滴:C# 根据字节长度截包含中文的字符串
    64位系统 IIS中应用程序池设置导致 访问数据库错误
  • 原文地址:https://www.cnblogs.com/chenmingjun/p/9986645.html
Copyright © 2011-2022 走看看