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

商城05——首页轮播图显示实现&Redis环境搭建&Redis实现缓存的更多相关文章

  1. celery介绍、架构、快速使用、包结构,celery执行异步、延迟、定时任务,django中使用celery,定时更新首页轮播图效果实现,数据加入redis缓存的坑及解决

    今日内容概要 celery介绍,架构 celery 快速使用 celery包结构 celery执行异步任务 celery执行延迟任务 celery执行定时任务 django中使用celery 定时更新 ...

  2. 用jQuery实现优酷首页轮播图

    ▓▓▓▓▓▓ 大致介绍 看到了一个轮播图的思路,就想的自己动手实践一下,总体来说用jQuery实现起来简单多了 如果对代码中使用的方法有疑问,可以参考我的jQuery学习之路(持续更新),里面有讲解: ...

  3. 关于用jQuery知识来实现优酷首页轮播图!

    ▓▓▓▓▓▓ 大致介绍 看到了一个轮播图的思路,就想的自己动手实践一下,总体来说用jQuery实现起来简单多了 如果对代码中使用的方法有疑问,可以参考我的jQuery学习之路(持续更新),里面有讲解: ...

  4. 【VIP视频网站项目二】搭建爱奇艺优酷腾讯视频官网首页轮播图效果及实现原理分析

    这个是实现的效果,基本上轮播效果和主流网站的一致,但是我也在上面优化了一些效果, 可以在线预览效果:https://vip.52tech.tech/ 目前项目代码已经全部开源:项目地址:https:/ ...

  5. web手工项目02-注册功能输入分析,处理,输出方法-测试用例及缺陷编写-首页轮播图和购物车

    web手工项目第二天笔记 昨日回顾 搭建测试环境(WAMP,phpStudy,tpshop项目文件) 熟悉项目(四个步骤,三个来源) 项目测试流程(需求评审,测试计划与方案,测试用例设计与评审,测试执 ...

  6. JavaScript学习——使用JS实现首页轮播图效果

    1.相关技术 获取元素 document.getElementById(“id 名称”) 事件(onload) 只能写一次并且放到body标签中 定时操作:setInterval(“changeImg ...

  7. Android自己定义控件之应用程序首页轮播图

    如今基本上大多数的Android应用程序的首页都有轮播图.就是像下图这种(此图为转载的一篇博文中的图.拿来直接用了): 像这种组件我相信大多数的应用程序都会使用到,本文就是自己定义一个这种组件,能够动 ...

  8. 【JavaScript】案例二:使用JS完成首页轮播图效果——事件(onclick&onload)

    1.切换图片例子: 事件(onclick) <!DOCTYPE html> <html> <head> <meta charset="UTF-8&q ...

  9. angularjs实现首页轮播图

    <!DOCTYPE html> <html ng-app="myApp" lang="en"> <head> <met ...

随机推荐

  1. Java——日期格式化YYYYMMdd与yyyyMMdd的区别

    public static void main(String[] args) { //YYYY 是表示:当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,那么这周就算入下一年. //20 ...

  2. 如何在Teamcenter中使用PMI?

    1 .什么是PMI 在设计制造领域,PMI指的是产品制造信息(Productand Manufacturing Information),其目的在于在三维环境下,将制造信息从设计部门传递到制造部门.其 ...

  3. 抽象类(abstract class)与抽象方法

    package cm.aff.abst; /* abstract:抽象的,,可以修饰类,方法 1.修饰类: 抽象类: ①不能被实例化 ②有构造器的 ③凡是类都有构造器 ④抽象方法所修饰的类一定是抽象类 ...

  4. 七个生产案例告诉你BATJ为何选择ElasticSearch!应用场景和优势!

    本文来源于公众号[胖滚猪学编程],转载请注明出处. 从今天开始,想和你一起死磕ElasticSearch,学习分布式搜索引擎,跟着胖滚猪就对了! 既然是ES的第一课,那么最重要的是让你爱上它!不想说那 ...

  5. 我天!xx.equals(null) 是什么骚操作??

    问题背景 我的天,最近做 Code Review 看到一个同事的骚操作,他写了一个工具类,大概是这样的: public static boolean isNull(Object object){ re ...

  6. ES6-常用四种数组

    1.map 1.1 个人理解 映射 一个对一个 例如:[45,57,138]与[{name:'blue',level:0},{name:'zhangsan',level:99},{name:'lisi ...

  7. SpringMVC(一)概述、解析器与注解

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.SpringMVC的概述 1.概述 Spring MVC框架是一个开源的Java平台,为开发强大的基 ...

  8. Spring ( 四 )Spring的AOP动态代理、切面编程

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.AOP切面编程 1.什么是AOP AOP是面向切面编程.全称:Aspect Oriented Pro ...

  9. Java实现 洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is He…

    import java.util.Scanner; public class Main{ private static Scanner cin; public static void main(Str ...

  10. Java实现 蓝桥杯 算法提高 道路和航路

    问题描述 农夫约翰正在针对一个新区域的牛奶配送合同进行研究.他打算分发牛奶到T个城镇(标号为1-T),这些城镇通过R条标号为(1-R)的道路和P条标号为(1-P)的航路相连. 每一条公路i或者航路i表 ...