SSM分布式项目之淘淘商城-第五天(IDEA)

文章大纲
一 、第五天课程计划
二、首页轮播图的展示
三、首页大广告展示流程图
四、Redis的介绍
五、Redis的安装
六、Redis持久化方案
七、Redis集群的搭建
八、Jedis的使用
九、向业务逻辑中添加缓存

淘淘商城课程大纲

课程大纲
一共14天课程
(1)第一天:电商介绍–互联网术语-SOA-分布式-集群介绍-环境配置-框架搭建
(2)第二天:Dubbo介绍_dubbo框架整合_商品列表查询实现_分页_逆向工程
(3)第三天:Git&.Nginx,类目选择,新增商品
(4)第四天:门户网站介绍&商城首页搭建&内容系统创建&CMS实现
(5)第五天:首页轮播图显示实现,Redis环境搭建,Redis实现缓存
(6)第六天:solr索引库搭建&solr搜索功能实现&图片显示问题解决
(7)第七天:solr集群搭建_全局异常处理
(8)第八天:activeMQ介绍_搭建_解决同步索引库问题
(9)第九天:FreeMark入门_静态化页面标签介绍_静态化页面实现
(10)第十天:Nginx代理详解…单点登录系统工程搭建_接口文档讲解
(11)第十一天:SSO系统的搭建&单点登录系统实现_用户名回显_cookie跨域问题
(12)第十二天:购物车订单系统的实现。
(13)第十三天:订单提交的功能实现&项目的部署&服务器的域名规划。
(14)项目总结。

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

分析:
portal-web 需要 自己自定义的POJO 的类型数据的列表
content 服务层 公用的,可以被其他的系统(表现的系统)来调用 ;

为了通用性:content 服务层中 获取 tbcontent的内容列表 即:list

portal-web 表现层 需要拿到tbcontent 的列表,然后进行转换成自定义的类型的数据列表即可。

从tb_content表中取数据,根据(叶子节点)内容分类id查询列表(内容列表)。
内容分类id固定,需要配置在属性文件中。
List

图片的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 中,返回给页面。

需要创建一个pojo转换成页面需要的json数据格式。放入com.taotao.portal.pojo中。
在这里插入图片描述

public class Ad1Node {

	private String srcB;
	private String height;
	private String alt;
	private String width;
	private String src;
	private String widthB;
	private String href;
	private String heightB;
   //getter/setter
}

2.2. Dao层

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

2.3. Service层

ContentService中添加方法
在这里插入图片描述

参数:long categoryId
返回值:List
在ContentServiceImpl中,部分代码如下:

@Override
	public List<TbContent> getContentList(long cid) {
		//根据cid查询内容列表
		TbContentExample example = new TbContentExample();
		//设置查询条件
		Criteria criteria = example.createCriteria();
		criteria.andCategoryIdEqualTo(cid);
		//执行查询
		List<TbContent> list = contentMapper.selectByExample(example);
		return list;
	}

然后 发布服务

2.4. 表现层

在taotao-portal-web中实现,查询首页轮播图的内容。
2.4.1. 引用服务
在taotao-protal-web项目中引入依赖包,如图:
在这里插入图片描述
然后引用服务

修改Controller

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

@Controller
public class PageController {
	@Autowired
	private ContentService contentService;
	
	@Value("${AD1_CATEGORY_ID}")
	private Long AD1_CATEGORY_ID;
	
	@Value("${AD1_HEIGHT}")
	private String AD1_HEIGHT;
	@Value("${AD1_HEIGHT_B}")
	private String AD1_HEIGHT_B;
	@Value("${AD1_WIDTH}")
	private String AD1_WIDTH;
	@Value("${AD1_WIDTH_B}")
	private String AD1_WIDTH_B;
	/**
	 * 展示首页
	 * 
	 * @return
	 */
	@RequestMapping("/index")
	public String showIndex(Model model) {
		//1.引入服务
		//2.注入服务
		//3.调用方法 tbcontent的列表
		List<TbContent> list = contentService.queryConentListByCategoryId(AD1_CATEGORY_ID);
		//4.转换成ad1node列表
		List<Ad1Node> nodes = new ArrayList<>();
		for (TbContent tbContent : list) {
			Ad1Node node = new Ad1Node();
			node.setAlt(tbContent.getSubTitle());
			node.setHref(tbContent.getUrl());
			node.setSrc(tbContent.getPic());
			node.setSrcB(tbContent.getPic2());
			node.setHeight(AD1_HEIGHT);
			node.setHeightB(AD1_HEIGHT_B);
			node.setWidth(AD1_WIDTH);
			node.setWidthB(AD1_WIDTH_B);
			nodes.add(node);
		}
		//5.转换成JSON数据
		String json = JsonUtils.objectToJson(nodes);
		//6.讲JSON数据设置到request域(MOdel)
		model.addAttribute("content_ad1", json);
		return "index";
	}
}

属性文件所在位置:
在这里插入图片描述

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

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

测试效果:
在这里插入图片描述

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)

4.4. redis的应用场景

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

https://redis.io/topics/benchmarks

在这里插入图片描述

5. Redis的安装

5.1. redis的下载

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

5.2. Redis的安装

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

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

如果有GCC环境,只需输入命令:

[root@localhost ~]# gcc

出现 :gcc: no input files 表示安装成功。

安装步骤:

第一步:将redis的源码包上传到linux系统。
第二步:解压缩redis的源码包。
第三步:进行编译。 cd到解压后的目录 输入命令:make   
第四步:进行安装。 输入命令:make install PREFIX=/usr/local/redis
PREFIX 必须是大写的。
 
第五步:检查目录是否存在。
在/usr/local/redis 下 有bin 说明安装成功。
 

在这里插入图片描述

在这里插入图片描述

5.3. 连接redis

redis服务端启动
前端启动

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

在这里插入图片描述

后台启动:

第一步:把/root/redis-3.0.0/redis.conf复制到/usr/local/redis/bin目录下
		[root@localhost redis-3.0.0]# cp redis.conf /usr/local/redis/bin/
第二步:使用vim命令修改redis.conf配置文件 将daemonize no修改为daemonize yes 

 
第三步:输入启动命令 
		[root@localhost bin]# ./redis-server redis.conf

第四步:检查redis进程:
		[root@localhost bin]# ps -ef|grep redis

在这里插入图片描述
在这里插入图片描述

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

客户端Redis-cli连接redis
使用Redis-cli建立连接:

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

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

[root@localhost bin]# ./redis-cli -h 192.168.25.153 -p 6379

-h:连接的服务器的地址
-p:服务的端口号

在这里插入图片描述

使用redis的桌面程序建立连接
先安装:
在这里插入图片描述在这里插入图片描述

退出连接:

	第一种:
	[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]#

关闭Redis服务

第一种:通过连接上客户端进行关闭,使用shutdown 命令。
在这里插入图片描述

或者:cd 到redis的bin 目录 再执行以下:

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

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

kill -9 pid

5.4. Redis五种数据类型

String:key-value
redis命令不区分大小写,但是key区分的
redis中的数据都是字符串。
redis是单线程,(不适合存储比较大的数据)

使用incr 命令,如果key 不存在,会自动创建key 并自动+1.
redis中所有的数据都是字符串。
set key value 设置值
get key 获取值
incr key 加一
decr key 减一
在这里插入图片描述

Hash: key-field-value

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

hset key field value 设置值
hget key field 获取值
hincrby key field num 设置增数量
在这里插入图片描述

List
List是有顺序可重复(数据结构中的:双链表,队列)
可作为链表 ,从左添加元素 也可以从右添加元素。

lpush list a b c d (从左添加元素)
rpush list 1 2 3 4 (从右边添加元素)
lrange list 0 -1 (从0 到 -1 元素查看:也就表示查看所有)
lpop list (从左边取,删除)
rpop list (从右边取,删除)
在这里插入图片描述

Set
Set无顺序,不能重复
sadd set1 a b c d d (向set1中添加元素) 元素不重复
smembers set1 (查询元素)
srem set1 a (删除元素)
在这里插入图片描述

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

zadd zset1 9 a 8 c 10 d 1 e (添加元素 zadd key score member )
(ZRANGE key start stop [WITHSCORES])(查看所有元素:zrange key 0 -1 withscores)

如果要查看分数,加上withscores.

zrange zset1 0 -1 (从小到大)

zrevrange zset1 0 -1 (从大到小)

zincrby zset2 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> zincrby zset1 1 a
"10"
127.0.0.1:6379> zrevrange zset1 0 -1 withscores
1) "a"
2) "10"
3) "c"
4) "5"
5) "b"
6) "4"
7) "d"
8) "1"

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

6. Redis持久化方案

Redis 数据都放在内存中。如果机器挂掉,内存的数据就不存在。
需要做持久化,讲内存中的数据保存在磁盘,下一次启动的时候就可以恢复数据到内存中。
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)

集群搭建环境

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

yum install ruby
yum install rubygems

2、上传redis-3.0.0.gem到 linux中
在这里插入图片描述
在这里插入图片描述

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

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

Successfully installed redis-3.0.0
1 gem installed
Installing ri documentation for redis-3.0.0…
Installing RDoc documentation for redis-3.0.0…
[root@localhost ~]#

查看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

搭建步骤
需要6台redis服务器。搭建伪分布式。
需要6个redis实例。
需要运行在不同的端口7001-7006

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

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

第一步:创建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

在这里插入图片描述

按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目录,修改每一个redis实例的端口分别改为7001-7006

[root@localhost bin]# vim redis.conf

第二步:启动每个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

第三步:使用ruby脚本搭建集群。
从解压目录下的src下的拷贝redis-trib.rb文件到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
[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
>>> Creating cluster
Connecting to node 192.168.25.153:7001: OK
Connecting to node 192.168.25.153:7002: OK
Connecting to node 192.168.25.153:7003: OK
Connecting to node 192.168.25.153:7004: OK
Connecting to node 192.168.25.153:7005: OK
Connecting to node 192.168.25.153:7006: OK
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.25.153:7001
192.168.25.153:7002
192.168.25.153:7003
Adding replica 192.168.25.153:7004 to 192.168.25.153:7001
Adding replica 192.168.25.153:7005 to 192.168.25.153:7002
Adding replica 192.168.25.153:7006 to 192.168.25.153:7003
M: 2e48ae301e9c32b04a7d4d92e15e98e78de8c1f3 192.168.25.153:7001
   slots:0-5460 (5461 slots) master
M: 8cd93a9a943b4ef851af6a03edd699a6061ace01 192.168.25.153:7002
   slots:5461-10922 (5462 slots) master
M: 2935007902d83f20b1253d7f43dae32aab9744e6 192.168.25.153:7003
   slots:10923-16383 (5461 slots) master
S: 74f9d9706f848471583929fc8bbde3c8e99e211b 192.168.25.153:7004
   replicates 2e48ae301e9c32b04a7d4d92e15e98e78de8c1f3
S: 42cc9e25ebb19dda92591364c1df4b3a518b795b 192.168.25.153:7005
   replicates 8cd93a9a943b4ef851af6a03edd699a6061ace01
S: 8b1b11d509d29659c2831e7a9f6469c060dfcd39 192.168.25.153:7006
   replicates 2935007902d83f20b1253d7f43dae32aab9744e6
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join.....
>>> Performing Cluster Check (using node 192.168.25.153:7001)
M: 2e48ae301e9c32b04a7d4d92e15e98e78de8c1f3 192.168.25.153:7001
   slots:0-5460 (5461 slots) master
M: 8cd93a9a943b4ef851af6a03edd699a6061ace01 192.168.25.153:7002
   slots:5461-10922 (5462 slots) master
M: 2935007902d83f20b1253d7f43dae32aab9744e6 192.168.25.153:7003
   slots:10923-16383 (5461 slots) master
M: 74f9d9706f848471583929fc8bbde3c8e99e211b 192.168.25.153:7004
   slots: (0 slots) master
   replicates 2e48ae301e9c32b04a7d4d92e15e98e78de8c1f3
M: 42cc9e25ebb19dda92591364c1df4b3a518b795b 192.168.25.153:7005
   slots: (0 slots) master
   replicates 8cd93a9a943b4ef851af6a03edd699a6061ace01
M: 8b1b11d509d29659c2831e7a9f6469c060dfcd39 192.168.25.153:7006
   slots: (0 slots) master
   replicates 2935007902d83f20b1253d7f43dae32aab9744e6
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
[root@localhost redis-cluster]# 

第四步 创建关闭集群的脚本:(不是必须的)

首先使用: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依赖的jar包添加到工程中。Maven工程中需要把jedis的坐标添加到依赖。

推荐添加到服务层。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() throws Exception {
		// 第一步:创建一个Jedis对象。需要指定服务端的ip及端口。
		Jedis jedis = new Jedis("192.168.25.153", 6379);
		// 第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。
		String result = jedis.get("hello");
		// 第三步:打印结果。
		System.out.println(result);
		// 第四步:关闭Jedis
		jedis.close();
	}

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

第一步:创建一个JedisPool对象。需要指定服务端的ip及端口。
第二步:从JedisPool中获得Jedis对象。
第三步:使用Jedis操作redis服务器。
第四步:操作完毕后关闭jedis对象,连接池回收资源。
第五步:关闭JedisPool对象。

@Test
	public void testJedisPool() throws Exception {
		// 第一步:创建一个JedisPool对象。需要指定服务端的ip及端口。
		JedisPool jedisPool = new JedisPool("192.168.25.153", 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();
	}

8.3. 连接集群版

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

@Test
	public void testJedisCluster() throws Exception {
		// 第一步:使用JedisCluster对象。需要一个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 = new JedisCluster(nodes);
		// 第二步:直接使用JedisCluster对象操作redis。在系统中单例存在。
		jedisCluster.set("hello", "100");
		String result = jedisCluster.get("hello");
		// 第三步:打印结果
		System.out.println(result);
		// 第四步:系统关闭前,关闭JedisCluster对象。
		jedisCluster.close();
	}

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

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

9.1. 接口封装

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

接口定义

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

单机版实现类

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

}

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:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans4.2.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context4.2.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx4.2.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util4.2.xsd">

	<!-- 配置单机版的连接 -->
	<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>
	<bean id="jedisClientPool" class="com.taotao.jedis.JedisClientPool"/>	
</beans>

集群版实现类

package com.taotao.jedis;

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

}

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.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>
	<bean id="jedisClientCluster" class="com.taotao.jedis.JedisClientCluster"/>

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

9.2. 封装代码测试

@Test
	public void testJedisClient() throws Exception {
		//初始化Spring容器
		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);				
	}

9.3. 添加缓存

功能分析
查询内容列表时添加缓存。

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

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

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

@Autowired
	private JedisClient jedisClient;	
	@Value("${CONTENT_KEY}")
	private String CONTENT_KEY;
@Override
	public List<TbContent> getContentList(long cid) {
		//查询缓存
		try {
			String json = jedisClient.hget(CONTENT_KEY, cid + "");
			//判断json是否为空
			if (StringUtils.isNotBlank(json)) {
				//把json转换成list
				List<TbContent> list = JsonUtils.jsonToList(json, TbContent.class);
				return list;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		//根据cid查询内容列表
		TbContentExample example = new TbContentExample();
		//设置查询条件
		Criteria criteria = example.createCriteria();
		criteria.andCategoryIdEqualTo(cid);
		//执行查询
		List<TbContent> list = contentMapper.selectByExample(example);
		//向缓存中添加数据
		try {
			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);
		//缓存同步
		jedisClient.hdel(CONTENT_KEY, content.getCategoryId().toString());
		
		return TaotaoResult.ok();
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值