1.   课程计划

1、首页轮播图的展示

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

3、Redis的常用命令

4、Redis的服务器搭建 (集群的搭建)

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

6、Jedis的使用(redis的客户端)

7、缓存同步

2.   首页轮播图展示

taotao-portal-web工程中,动态展示内容信息。

前端团队:负责JS,html等开发。

后端团队:负责后台的开发并提供数据给前端。

2.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
}
]

分析:

  需要 自己自定义POJO 存放数据的列表,在 portal-web 中定义。

  由于content 服务层 是公用的,可以被其他的系统(表现的系统)来调用,所以我们需要的POJO不能再service层返回,

  所以为了通用性:我们从content 服务层中 获取 tbcontent的内容列表 即 list<TbContent>;portal-web 表现层 需要拿到tbcontent 的列表,然后进行转换成自定义的类型的数据列表即可。

从tb_content表中取数据,根据(叶子节点)内容分类id查询列表(内容列表)。List<TbContent>

内容分类id固定,需要配置在属性文件中。

图片的width、height配置在属性文件中。

Alt属性从sub_title中取。

  alt-->sub_title

  Src->pic

  srcB->pic2

  Href->url

分析:

  URL:/index

  参数:无。

  返回值:首页页面   (数据是JSON 设置model中)

业务逻辑:

  1、根据分类的id 查询 内容列表(List<TbContent>)

   2、发布服务

  3、表现层引入服务

  4、调用服务 ,转换成自定义的数据类型(Ad1Node) 的列表

  5、将数据列表设置到Model 中,返回给页面。

2.2 后台实现

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

/**
* 首页大广告位轮播图数据展示POJO
*/
public class Ad1Node {
private String srcB; //pic2
private String height;
private String alt; //subtitle中获取
private String width;
private String src; //pic
private String widthB;
private String href; //url
private String heightB;
set/get。。。。。。
}

2.2.1 Dao层

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

2.2.2 Service层

    参数    :long categoryId

    返回值:List<TbContent>

接口:(taotao-content-interface工程下com.taotao.content.service包下的 ContentService接口类:)

  /**
* 根据cid查询内容列表
*/
public List<TbContent> getContentListByCatId(long cid);

实现类:(taotao-content-service工程下com.taotao.content.service.impl包下的 ContentServiceImpl 实现类:)

  @Override
public List<TbContent> getContentListByCatId(long cid) {
TbContentExample example = new TbContentExample();
Criteria criteria = example.createCriteria();
criteria.andCategoryIdEqualTo(cid);
//执行查询
List<TbContent> list = contentMapper.selectByExample(example);
return list;
}

发布服务:(applicationContext-service.xml)前面已经发布过了

2.2.3 Controller层

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

引用服务:

  首先在taotao-protal-web项目中引入taotao-content-interface的依赖包

  然后在taotao-protal-web项目下的 springmvc.xml 配置文件中引用dubbo服务:

  <!-- 引用dubbo服务 -->
<dubbo:application name="taotao-portal-web" />
<dubbo:registry protocol="zookeeper" address="192.168.25.129:2181" />
<dubbo:reference interface="com.taotao.content.service.ContentService" id="contentService" timeout="300000" />

Controller:

  在首页展示之前,对数据进行处理,然后展示首页,需要在PageController中实现。以大广告位为例子。

  

@Controller
public class PageController { //注入服务
@Autowired
private ContentService contentService; @Value("${AD1_CATEGORY_ID}")
private Long categoryId; @Value("${AD1_HEIGHT_B}")
private String AD1_HEIGHT_B;
@Value("${AD1_HEIGHT}")
private String AD1_HEIGHT;
@Value("${AD1_WIDTH}")
private String AD1_WIDTH;
@Value("${AD1_WIDTH_B}")
private String AD1_WIDTH_B; /**
* 展示首页
* @return
*/
@RequestMapping("/index")
public String showIndex(Model model){
//根据内容分类的id查询内容列表
List<TbContent> contentList = contentService.getContentListByCatId(categoryId);
//转成自定义的pojo:Ad1Node
List<Ad1Node> nodes = new ArrayList<Ad1Node>();
for(TbContent content : contentList){
Ad1Node node = new Ad1Node();
node.setAlt(content.getSubTitle());
node.setHeight(AD1_HEIGHT);
node.setHeightB(AD1_HEIGHT_B);
node.setHref(content.getUrl());
node.setSrc(content.getPic());
node.setSrcB(content.getPic2());
node.setWidth(AD1_WIDTH);
node.setWidthB(AD1_WIDTH_B); nodes.add(node);
}
//传递数据给jsp
model.addAttribute("ad1", JsonUtils.objectToJson(nodes)); return "index"; //响应jsp
}
}

属性文件所在位置:

在taotao-portal-web中的springmvc.xml中还需要配置:

<!-- 加载属性文件 -->
<context:property-placeholder location="classpath:resource/*.properties" />

注意在index.jsp页面修改接收数据为“ad1”:

测试效果:

3.   首页大广告的展示流程

  首页是系统的门户,也就是系统的入口。所以首页的访问量是这个系统最大的。如果每次展示首页都从数据库中查询首页的内容信息,那么势必会对数据库造成很大的压力,所以需要使用缓存来减轻数据库压力。

  实现缓存的工具有很多,现在比较流行的是redis

  首页大广告展示流程:

    展示流程: 先从缓存取 ,如果不存在 ,从数据库取出来,写入缓存,再返回页面;如果存在key ,直接从缓存中取出来,展示到页面。

    

    同步缓存:当事务提交(更新,删除,插入)后,需要同步缓存,直接根据Key 删除redis的key(清空缓存) ,再展示时 由上边的流程展示。

    

4. redis的介绍

4.1. 什么是NoSql

为了解决高并发、高可扩展(集群)、高可用(不能宕机)、大数据存储问题而产生的数据库解决方案,就是NoSql数据库。

NoSql  :全称 not only sql ,非关系型数据库可以作为关系型数据库的一个很好的补充,不能替代关系型数据库。

4.2. NoSql数据库分类

  • 键值(Key-Value)存储数据库

  相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB。

  典型应用:内容缓存,主要用于处理大量数据的高访问负载。

  数据模型:一系列键值对

  优势:快速查询

  劣势:存储的数据缺少结构化

  • 列存储数据库

  相关产品:Cassandra, HBase, Riak

  典型应用:分布式的文件系统

  数据模型:以列簇式存储,将同一列数据存在一起

  优势:查找速度快,可扩展性强,更容易进行分布式扩展

  劣势:功能相对局限

  • 文档型数据库

  相关产品:CouchDB、MongoDB

  典型应用:Web应用(与Key-Value类似,Value是结构化的)

  数据模型:一系列键值对

  优势:数据结构要求不严格

   劣势:查询性能不高,而且缺乏统一的查询语法

  • 图形(Graph)数据库

  相关数据库:Neo4J、InfoGrid、Infinite Graph

  典型应用:社交网络

  数据模型:图结构

  优势:利用图结构相关算法。

  劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。

4.3. 什么是redis

Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库(nosql),应用在缓存。它通过提供多种键值数据类型来适应不同场景下的存储需求

  目前为止Redis支持的键值数据类型有5种。如下:

    字符串类型 (String)

    散列类型(hash)

    列表类型(List)

    集合类型(set)

    有序集合类型(SortedSet)

1.4. redis的应用场景

  • 缓存(最重要的应用场景)
  • 分布式集群架构中的session分离
  • 任务队列    (秒杀、抢购、12306等等)
  • 应用排行榜 (SortedSet)
  • 网站访问统计
  • 数据过期处理  (expire)

redis官网:https://redis.io/topics/benchmarks

5. Redis的安装

5.1 Redis的安装

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

安装步骤:

  第1步:将redis的源码包上传到linux系统。

      Alt+p打开sftp窗口:输入put "F:/java/ziyuan/redis-3.0.0.tar.gz"

  第2步:解压:tar -zxvf redis-3.0.0.tar.gz

  第3步:进行编译。 cd到解压后的目录 输入命令:make

  第4步:进行安装。 输入命令:make install PREFIX=/usr/local/redis      (注意PREFIX 必须是大写的。)

      

  第5步:检查目录是否存在。

      在/usr/local/redis 下 有bin 说明安装成功。

      

5.2. 连接redis

5.2.1.redis服务端启动

  • 前端启动   [root@localhost bin]# ./redis-server

    Ctrl+c退出编辑

  一般不使用前端启动,而使用后端启动。

  • 后台启动:

  第1步:把/root/redis-3.0.0/redis.conf复制到/usr/local/redis/bin目录下

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

  第2步:使用vim命令修改redis.conf配置文件,开始编辑文本,vim redis.conf后点击 i 键进入编辑(此时在窗体最底下会出现— —insert),将daemonize no修改为daemonize yes 。

      输入完成后按ESC键退出,然后保存编辑的文件并退出。命令: :wq

      

  第3步:输入启动命令

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

  第4步:检查redis进程:

[root@localhost bin]# ps -ef|grep redis

    

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

5.2.2.客户端Redis-cli连接redis

  • 使用Redis-cli建立连接: [root@localhost bin]# ./redis-cli

    默认连接localhost运行在6379端口的redis服务。

    [root@localhost bin]# ./redis-cli -h 192.168.25.131 -p 6379     (完整的连接服务端的命令,-h:连接的服务器的地址,-p:服务的端口号)

  • 使用redis的桌面程序redis-desktop-manager建立连接

    redis-desktop-manager安装及使用参照https://www.jianshu.com/p/6895384d2b9e

  • 退出连接:

    第一种:

       [root@localhost bin]# ./redis-cli

    127.0.0.1:6379> quit

   第二种:

     [root@localhost bin]# ./redis-cli

    127.0.0.1:6379> exit

   第三种:CTR+C

      [root@localhost bin]#

5.2.3. 关闭Redis服务

  第一种:通过连接上客户端进行关闭,使用shutdown 命令。

      

      或者:cd 到redis的bin 目录  再执行以下:[root@localhost bin]# ./redis-cli shutdown 

  第二种:使用 kill 命令。

      找到对应的redis的进程id 然后使用命令:(pid为进程id)   kill -9 pid

5.3. Redis五种数据类型

5.3.1.String:key-value

  redis命令不区分大小写,但key是区分大小的。

  redis中的数据都是字符串。

  redis是单线程,(不适合存储比较大的数据)

  redis中所有的数据都是字符串。

  set key value  设置值

  get key            获取值

   incr key           加一(使用incr  命令,如果key 不存在,会自动创建key 并自动+1)

  decr key          减一

  

5.3.2. Hash: key-field-value

相当于一个key 对应一个map (map中又是key- value),应用于归类。

hset  key field value    设置值

  hget  key field              获取值

hincrby key field num  设置增数量

  

5.3.3. List

       List是有顺序可重复(数据结构中的:双链表,队列)

可作为链表 ,从左添加元素  也可以从右添加元素。

  lpush list a b c d     (从list左边添加元素)

   rpush list 1 2 3 4     (从list右边添加元素)

  lrange list 0 -1         (从0 到 -1 元素查看:也就表示查看所有)

  lpop list                 (从list左边取,删除)

   rpop list                   (从list右边取,删除)

    

5.3.4. Set

  Set无顺序,不能重复

  sadd set1 a b c d d    (向set1中添加元素) 元素不重复

  smembers set1        (查询元素)

  srem set1 a              (删除元素)

  

5.3.5. SortedSet(zset)

       有顺序,不能重复。    适合做排行榜 排序需要一个分数属性

  zadd zset1 1 a 2 b 3 c            (添加元素 zadd key score member,这里添加元素a:1分、元素b:2分、元素c:3分 )

  zrange zset1  0  -1                    (查看zset1的所有元素,默认从小到大)

  zrange zset1  0  -1  withscores  (查看zset1的所有元素,包括分数score)

  zrevrange zset1 0 -1                  (查看zset1的所有元素,从大到小)

  zincrby zset1 5 a                        (对zset1的a元素增加5分)

  

5.4. 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号数据库

6.  Redis持久化方案

Redis 数据都放在内存中如果机器挂掉,内存的数据就不存在。

需要做持久化,将内存中的数据保存在磁盘,下一次启动的时候就可以恢复数据到内存中。

 2种持久化方案:

1、RDB   快照形式  (定期将当前时刻的数据保存磁盘中)会产生一个dump.rdb文件

特点:会存在数据丢失,性能较好,数据备份。

2、AOF   append only file  (所有对redis的操作命令记录在aof文件中),恢复数据,重新执行一遍即可。

特点:每秒保存,数据比较完整,耗费性能。

redis 默认开启RDB。

如下图:redis.conf中默认设置了保存规则及时间间隔

AOF开启设置:

修改 redis.conf 文件  如下图:

将appendonly 设置为yes

同时开启两个持久化方案,则按照 AOF的持久化放案恢复数据。

默认是按照rdb的方式恢复数据,如果开启了AOF,就是用AOF恢复数据,数据是存在于/usr/local/redis/bin/appendonly.aof文件中

7.  Redis集群的搭建

7.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 会根据节点数量大致均等的将哈希槽映射到不同的节点。

  

7.2. Redis集群的搭建

  至少3个节点,为了集群的高可用,为每一个节点增加一个备份机。(6台服务器)。

  搭建伪分布式集群方案:在一台机器里面运行6个redis实例。端口需要不同(7001-7006)

7.2.1.集群搭建环境

1、使用ruby脚本搭建集群。需要ruby的运行环境。

2、上传redis-3.0.0.gem到 linux中

  Alt+p打开sftp窗口:输入put "F:/java/ziyuan/redis-3.0.0.gem"

3、安装ruby运行时所使用的包

  [root@localhost ~]# gem install redis-3.0.0.gem

  查看redis-trib.rb 脚本所在的目录:

    [root@localhost ~]# cd redis-3.0.0/src

    [root@localhost src]# ll *.rb

    -rwxrwxr-x. 1 root root 48141 Apr  1  2015  redis-trib.rb

7.2.2. 搭建步骤

  需要6台redis服务器。搭建伪分布式。

  需要6个redis实例。

  需要运行在不同的端口7001-7006

  使用之前搭建好的redis实例 。

  注意:搭建前 如果节点里有数据,需要删除(rdb文件,aof文件)。

第1步:创建6个redis实例,每个实例运行在不同的端口。需要修改redis.conf配置文件。配置文件中还需要把cluster-enabled yes前的注释去掉。

  • 创建目录:[root@localhost local]# mkdir redis-cluster
  • copy 之前搭建好的redis 并改名为redis01 :[root@localhost local]# cp redis/ redis-cluster/redis01 -r
  • 进入redis-cluster目录中cd到redis01的bin目录,删除数据文件 。

  [root@localhost local]# cd redis-cluster/redis01/bin

  [root@localhost bin]# rm -rf *.rdb *.aof

  • 修改redis.conf,取消注释,如图

  [root@localhost bin]# vim redis.conf

  

  更改端口号为7001:

  

  按ESC    :wq     保存退出。

  • cd到redis-cluster目录
  • copy六份并分别命名为redis02,redis03,redis04,redis05,redis06

  [root@localhost redis-cluster]# cp redis01 redis02 -r

  [root@localhost redis-cluster]# cp redis01 redis03 -r

  [root@localhost redis-cluster]# cp redis01 redis04 -r

  [root@localhost redis-cluster]# cp redis01 redis05 -r

  [root@localhost redis-cluster]# cp redis01 redis06 -r

  • cd到每一个实例的bin目录,修改redis02到redis06的端口分别改为7002-7006

  [root@localhost redis-cluster]# vim redis02/bin/redis.conf

  

第2步:启动每个redis实例。

  vim  redis-cluster-start-all.sh

  添加如下文字到文件中:

    cd /usr/local/redis-cluster/redis01/bin

    ./redis-server redis.conf

    cd /usr/local/redis-cluster/redis02/bin

    ./redis-server redis.conf

    cd /usr/local/redis-cluster/redis03/bin

    ./redis-server redis.conf

    cd /usr/local/redis-cluster/redis04/bin

    ./redis-server redis.conf

    cd /usr/local/redis-cluster/redis05/bin

    ./redis-server redis.conf

    cd /usr/local/redis-cluster/redis06/bin

    ./redis-server redis.conf

  • 修改文件:redis-cluster-start-all.sh 的权限,让其可执行。

    chmod u+x redis-cluster-start-all.sh

  • 执行启动 :[root@localhost redis-cluster]# ./redis-cluster-start-all.sh

第3步:使用ruby脚本搭建集群。

  • 从解压目录下的src下的拷贝redis-trib.rb文件到redis-cluster目录中

  

  [root@localhost src]# cp redis-trib.rb  /usr/local/redis-cluster/

  • 执行创建:

  [root@localhost 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步: 创建关闭集群的脚本:(不是必须的)

  首先使用:vim 命令创建一个文件 redis-cluster-stop-all.sh

  编辑文件,添加如下:

    cd /usr/local/redis-cluster/redis01/bin

    ./redis-cli -p 7001 shutdown

    cd /usr/local/redis-cluster/redis02/bin

    ./redis-cli -p 7002 shutdown

    cd /usr/local/redis-cluster/redis03/bin

    ./redis-cli -p 7003 shutdown

    cd /usr/local/redis-cluster/redis04/bin

    ./redis-cli -p 7004 shutdown

    cd /usr/local/redis-cluster/redis05/bin

    ./redis-cli -p 7005 shutdown

    cd /usr/local/redis-cluster/redis06/bin

    ./redis-cli -p 7006 shutdown

  修改文件:redis-cluster-stop-all.sh 的权限,让其可执行 :chmod u+x redis-cluster-stop-all.sh

7.3. 集群的使用方法

  Redis-cli连接集群:[root@localhost redis-cluster]# redis01/bin/redis-cli -p 7002 -c

  (-c:代表连接的是redis集群)

  使用命令操作redis是和单机版的一样。

8.  Jedis

  Jedis是Redis官方推出的一款面向Java的客户端,提供了很多接口供Java语言调用。可以在Redis官网下载,当然还有一些开源爱好者提供的客户端,如Jredis、SRP等等,推荐使用Jedis。

  需要把jedis依赖的jar包添加到工程中。Maven工程中需要把jedis的坐标添加到依赖。

  添加缓存(添加图片 首页的轮播图,轮播图属于内容content)

  推荐添加到服务层。Taotao-content-Service工程中。

  添加如下坐标到taotao-content-service中的pom.xml文件中:

       <!-- Redis客户端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>

8.1. 连接单机版

第一步:创建一个Jedis对象。需要指定服务端的ip及端口。

第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。

第三步:打印结果。

第四步:关闭Jedis

  /**
* 测试单机版
*/
@Test
public void testJedis(){
//1.创建Jedis对象 需要指定连接的地址和端口
Jedis jedis = new Jedis("192.168.25.131",6379);
//2.直接操作Jedis set
jedis.set("key123", "value123");
System.out.println(jedis.get("key123"));
//3.关闭Jedis
jedis.close();
}
输出:value123

8.2. 使用连接池连接单机版

第一步:创建一个JedisPool对象。需要指定服务端的ip及端口。

第二步:从JedisPool中获得Jedis对象。

第三步:使用Jedis操作redis服务器。

第四步:操作完毕后关闭jedis对象,连接池回收资源。

第五步:关闭JedisPool对象。

  /**
* 使用连接池连接单机版
*/
@Test
public void testJedisPool(){
// 第一步:创建一个JedisPool对象。需要指定服务端的ip及端口。
JedisPool jedisPool = new JedisPool("192.168.25.131", 6379);
// 第二步:从JedisPool中获得Jedis对象。
Jedis jedis = jedisPool.getResource();
// 第三步:使用Jedis操作redis服务器。
jedis.set("jedis", "test");
String result = jedis.get("jedis");
System.out.println(result);
// 第四步:操作完毕后关闭jedis对象,连接池回收资源。
jedis.close();
// 第五步:关闭JedisPool对象。
jedisPool.close();
}
输出:test

8.3. 连接集群版

第一步:使用JedisCluster对象。需要一个Set<HostAndPort>参数。Redis节点的列表。

第二步:直接使用JedisCluster对象操作redis。在系统中单例存在。

第三步:打印结果

第四步:系统关闭前,关闭JedisCluster对象。

/**
* 连接集群版
*/
@Test
public void testJedisCluster() throws Exception {
//1.创建JedisCluster对象
//需要一个Set<HostAndPort>参数。Redis节点的列表。
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.25.131", 7001));
nodes.add(new HostAndPort("192.168.25.131", 7002));
nodes.add(new HostAndPort("192.168.25.131", 7003));
nodes.add(new HostAndPort("192.168.25.131", 7004));
nodes.add(new HostAndPort("192.168.25.131", 7005));
nodes.add(new HostAndPort("192.168.25.131", 7006));
JedisCluster jedisCluster = new JedisCluster(nodes);
//2.直接根据JedisCluster对象操作redis集群
jedisCluster.set("hello", "100");
//3.打印结果
System.out.println(jedisCluster.get("hello"));
//4.系统关闭前,关闭JedisCluster对象
jedisCluster.close();
}
输出:100

9.   向业务逻辑中添加缓存

因为集群是比较消耗成本的,所以在公司中,一般生产环境使用集群,开发环境使用单机版。

我们在项目整合中都需要有。

可以开发一个接口,有单机版的实现类和集群版的实现类。使用时可以面向接口开发,不影响业务逻辑,使用spring管理实现类,部署时切换实现类即可。

9.1. 接口封装

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

9.1.1.接口定义

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);
}

9.1.2.单机版实现类

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;
} }

9.1.3.applicationContext-redis.xml

<!-- 配置单机版的连接 -->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg name="host" value="192.168.25.131"></constructor-arg>
<constructor-arg name="port" value="6379"></constructor-arg>
</bean>
<bean id="jedisClientPool" class="com.taotao.jedis.JedisClientPool"/>
</beans>

9.1.4.集群版实现类

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);
} }

9.1.5.Spring的配置:

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

  <!-- 集群版的配置 -->
<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.131"></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.131"></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.131"></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.131"></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.131"></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.131"></constructor-arg>
<constructor-arg name="port" value="7006"></constructor-arg>
</bean>
</set>
</constructor-arg>
</bean>
<bean id="jedisClientCluster" class="com.taotao.jedis.JedisClientCluster"/>

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

9.2. 封装代码测试

  @Test
public void testdanji(){
//1.初始化spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-redis.xml");
//2.获取实现类实例
JedisClient bean = context.getBean(JedisClient.class);
//3.调用方法操作
bean.set("jedisclientkey11", "jedisclientkey");
System.out.println(bean.get("jedisclientkey11"));
}

9.3. 添加缓存

9.3.1.功能分析

查询内容列表时添加缓存

  1、查询数据库之前先查询缓存。

  2、查询到结果,直接响应结果。

  3、查询不到,缓存中没有需要查询数据库。

  4、把查询结果添加到缓存中。

  5、返回结果。

向redis中添加缓存:

  Key:categoryId  field

  Value:内容列表。需要把java对象转换成json。

  使用hash对key进行归类。

  HASH_KEY:HASH

   |--KEY:VALUE

  |--KEY:VALUE

  |--KEY:VALUE

  |--KEY:VALUE

注意:添加缓存不能影响正常业务逻辑。

9.3.2.代码实现

在 ContentServiceImpl 实现类中的 getContentListByCatId方法(查询内容列表)添加缓存:

//注入jedisClient
@Autowired
private JedisClient jedisClient; @Value("${CONTENT_KEY}")
private String CONTENT_KEY;
//////////根据cid查询内容列表
@Override
public List<TbContent> getContentListByCatId(long cid) { //查询缓存。判断是否redis中有数据,如果有,直接从redis中获取数据返回
try{
String jsonstr = jedisClient.hget(CONTENT_KEY, cid+"");//从redis数据库中获取内容分类下的所有内容
if(StringUtils.isNoneBlank(jsonstr)){//不为空
//System.out.println("这里有缓存了!!!");
return JsonUtils.jsonToList(jsonstr, TbContent.class);
}
}catch(Exception e){
e.printStackTrace();
}
//创建example
TbContentExample example = new TbContentExample();
//设置查询条件
Criteria criteria = example.createCriteria();
criteria.andCategoryIdEqualTo(cid);
//执行查询
List<TbContent> list = contentMapper.selectByExample(example); //将数据写入到redis数据库:
try{
//System.out.println("没有缓存!");
jedisClient.hset(CONTENT_KEY, cid+"", JsonUtils.objectToJson(list));
}catch(Exception e){
e.printStackTrace();
} //返回
return list;
}

属性文件所在的位置:

要想能加载还需要在applicationContext-dao.xml中配置:

<context:property-placeholder location="classpath:properties/*.properties" />

9.4. 缓存同步

对内容信息做增删改操作后只需要把对应缓存key删除即可。

以添加内容为例,可以根据categoryId删除。

    ///////////添加内容
@Override
public TaotaoResult addContent(TbContent content) {
//补全属性
content.setCreated(new Date());
content.setUpdated(new Date());
//插入数据
contentMapper.insert(content); //当添加内容的时候,需要清空此内容所属的分类下的所有的缓存
try{
jedisClient.hdel(CONTENT_KEY, content.getCategoryId()+"");
System.out.println("当插入时清空缓存!");
}catch(Exception e){
e.printStackTrace();
} //返回结果
return TaotaoResult.ok();
}

最新文章

  1. JavaScript学习笔记6 之经典神坑题整理
  2. SSH和SSM项目的打通各个页面的方式
  3. C++基础练习题(一): 查找最短单词
  4. javascript的onbeforeunload函数在IOS上运行
  5. eclipse 下调整jdk和tomcat的jvm参数
  6. 使用air进行移动app开发常见功能和问题(二)
  7. Java笔记(十四)&hellip;&hellip;抽象类与接口
  8. bzoj1197
  9. c++中 cin、cin.get()、cin.getline()、cin.getchar()的区别
  10. Oracle 11g新特性invisible index(不可见的索引)
  11. 微信小程序 - 自定义创建
  12. 常见JedisConnectionException异常分析
  13. 《15个提高Google搜索的技巧》
  14. 蓝图Tips
  15. 基于ASP.NET的高校辅导员工作管理系统的设计与实现--论文随笔(四)
  16. [您有新的未分配科技点][BZOJ3545&amp;BZOJ3551]克鲁斯卡尔重构树
  17. 【转】Windows守护进程的一种简单实现
  18. list集合与HashMap的使用
  19. android 4.0 webview 无法播放视频
  20. python set() 集合的添加删除、交集、并集、差集、交叉补集、集合的方法介绍以及使用案例

热门文章

  1. javascript代码重构需要考虑的问题(一)
  2. CSS3中的rem单位
  3. 0511Object类和异常
  4. 谈谈MySQL 索引
  5. uniapp轻轻松松开发各种类型的小程序
  6. 上位机开发之三菱FX3U以太网通信实践
  7. 国家集训队 部落战争 网络流最小路径覆盖 洛谷P2172
  8. Vue 哈希换histroy
  9. 基于nodejs+express+mysql+webstorm+html的 增删改查
  10. 使用setTimeout()代替setInterval()